Ответ 1
Это полностью зависит от объекта i
.
+=
вызывает метод __iadd__
(если он существует - возвращается на __add__
, если он не существует) тогда как +
вызывает метод __add__
1 или __radd__
в нескольких случаях 2.
С точки зрения 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
ссылаются на один и тот же объект, когда я использую +=
на 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__
или __iadd__
, тогда результат здесь будет таким же, как
foo_instance = bar_instance.__radd__(bar_instance, foo_instance)
2 В выражении foo_instance + bar_instance
, bar_instance.__radd__
будет проверяться до foo_instance.__add__
, если тип bar_instance
является подклассом типа foo_instance
(например, issubclass(Bar, Foo)
). Рациональным для этого является то, что Bar
в некотором смысле является объектом более высокого уровня, чем Foo
, поэтому Bar
должен получить возможность переопределения поведения Foo
.