Строка, как
x += y
фактически переведено в эквивалент
x = x.__iadd__(y)
Переплет всегда происходит, даже если x
изменчив. Если __iadd__()
реализован способом, выполняющим операцию на месте, он должен вернуть self
, и имя в любом случае возвращается к объекту, на которое оно указывало. Если __iadd__()
не реализовано, вместо него используются обычные методы сложения __add__()
или __radd__()
, как вы уже отмечали в своем посте.
Именно поэтому x += y
внутри функции отображает x
локальное имя. (Объяснение: В Python имя считается локальным для функции, если внутри функции есть присвоение этому имени. x += y
считается присваиванием x
, поэтому x
считается локальным для функции, содержащей такие см. эту запись в блоге Эли Бендерски для получения дополнительной информации.)
Еще один пример такого поведения:
t = ([], 1)
t[0] += [1]
приводит к ошибке
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
но список все равно добавляется. Это потому, что сначала вызывается list.__iadd__()
, меняя список на месте. Затем предпринимается попытка t[0] = <list object>
, что приводит к ошибке.
Подробную документацию по семантике можно найти в Справочнике по языку Python и в PEP 203 . Цитата из бывшего:
Расширенное назначение оценивает цель (которая, в отличие от обычных операторов назначения, не может быть распаковкой) и список выражений, выполняет двоичную операцию, специфичную для типа назначения для двух операндов, и присваивает результат исходной цели , Цель оценивается только один раз.
Расширенное выражение присваивания, такое как x + = 1, может быть переписано как x = x + 1 для достижения аналогичного, но не совсем равного эффекта. В расширенной версии x оценивается только один раз. Кроме того, когда это возможно, фактическая операция выполняется на месте, что означает, что вместо создания нового объекта и присвоения его цели, вместо этого изменяется старый объект.