С точки зрения API, __iadd__ предполагается использовать для модификации изменяемых объектов на месте (возвращая объект, который был изменен), тогда как __add__ должен возвращать новый экземпляр чего-либо. Для неизменяемых объектов оба метода возвращают новый экземпляр, но __iadd__ поместят новый экземпляр в текущее пространство имен с тем же именем, что и у старого экземпляра. Вот почему
i = 1 i += 1
кажется, увеличивается i. На самом деле вы получаете новое целое число и присваиваете его "поверх" i - теряя одну ссылку на старое целое число. В этом случае i += 1 точно такой же, как i = i + 1. Но с большинством изменяемых объектов это другая история:
В качестве конкретного примера:
a = [1, 2, 3] b = a b += [1, 2, 3] print(a) # [1, 2, 3, 1, 2, 3] print(b) # [1, 2, 3, 1, 2, 3]
по сравнению с:
a = [1, 2, 3] b = a b = b + [1, 2, 3] print(a) # [1, 2, 3] print(b) # [1, 2, 3, 1, 2, 3]
обратите внимание, как в первом примере, поскольку b и a ссылаются на один и тот же объект, когда я использую += on b, он фактически меняется b (и a тоже видит это изменение - в конце концов, он ссылается на тот же список). Однако во втором случае, когда я делаю b = b + [1, 2, 3], это берет список, на который b ссылается, и объединяет его с новым списком [1, 2, 3]. Затем он сохраняет объединенный список в текущем пространстве имен как b -- Без учета того, какой b была строка раньше.
1в выражение x + y, если x.__add__ не реализовано или если x.__add__(y) возвращает NotImplementedиx и y имеют разные типы, то x + y пытается дозвониться y.__radd__(x). Итак, в случае, когда у вас есть
foo_instance += bar_instance
если Foo не реализует __add__ or __iadd__ , то результат здесь такой же, как
2В выражении foo_instance + bar_instance, bar_instance.__radd__ будет опробовано ранее, foo_instance.__add__если тип bar_instance является подклассом типа foo_instance (например, issubclass(Bar, Foo)). Обоснование этого заключается в том, что Bar в некотором смысле является объектом "более высокого уровня", чем Foo поэтому Bar должен получить возможность переопределения Foo поведения.
Ответ 2
Под прикрытием, i += 1 делает что-то вроде этого:
try: i = i.__iadd__(1) except AttributeError: i = i.__add__(1)
While i = i + 1 делает что-то вроде этого:
i = i.__add__(1)
Это небольшое упрощение, но вы поняли идею: Python предоставляет типам способ обработки += особым образом, создавая __iadd__ метод, а также __add__ .
Предполагается, что изменяемые типы, такие как list, будут изменять себя в __iadd__ (и затем возвращать self, если вы не делаете что-то очень сложное), в то время как неизменяемые типы, такие как int, просто не будут это реализовывать.
Например:
>>> l1 = [] >>> l2 = l1 >>> l1 += [3] >>> l2 [3]
Поскольку l2 это тот же объект, что и l1, и вы мутировали l1, вы также мутировали l2.
Здесь вы не мутировали l1; вместо этого вы создали новый список, l1 + [3] и изменили имя, l1 чтобы оно указывало на него, оставив l2 указание на исходный список.
(В += версии вы также выполняли повторную привязку l1, просто в этом случае вы повторно привязывали его к тому же list, к которому он уже был привязан, поэтому вы обычно можете игнорировать эту часть.)
Ответ 3
Вот пример, который напрямую сравнивает i += x с i = i + x:
deffoo(x): x = x + [42]
defbar(x): x += [42]
c = [27] foo(c); # c is not changed bar(c); # c is changed to [27, 42]