Поведение a += и a = a + для списков в Python

Поведение a += и a = a + для списков в Python

@python_quiz

Разберем этот квиз

Коротко: одинаково выглядящие записи могут вести себя по‑разному из‑за мутабельности объектов и того, как реализованы операции в Python. Разберём шаги, покажем код и объясним, почему конечный вывод такой, а не другой.

Условие (код)

python

Пошаговый разбор

  1. a = [1] — создаётся список [1].
  2. b = a — b и a ссылаются на один и тот же объект (алиас).
  3. a += [2]

- Для списков оператор += вызывает метод __iadd__, который мутирует список на месте (расширяет существующий объект).
- Поскольку a и b указывают на один объект, изменения видны и через b.
- После этой строки список будет [1, 2] и id(a) == id(b).

  1. a = a + [3]

- Оператор + для списков создаёт новый список (результат __add__), не мутируя исходный.
- Присваивание a = ... перенаправляет переменную a на новый объект, b остаётся указывать на старый.
- В результате a становится [1, 2, 3], а b остаётся [1, 2].

Итоговый вывод: [1, 2, 3] [1, 2]

Демонстрация с id(), чтобы увидеть смену объектов

python

Ожидаемый вывод (ids будут отличаться на вашей машине, пример):

Почему это важно

  • Операция += может быть in-place (мутация) для мутабельных типов (списки, bytearray и т.д.), что изменяет все ссылки на объект.
  • Операция + обычно создаёт новый объект (для списков возвращается новый список).
  • Поведение += зависит от того, реализован ли у объекта __iadd__. Если __iadd__ отсутствует или возвращает NotImplemented, Python прибегает к __add__ и создаёт новый объект.

Примеры отличий:

  • Для immutable типов (кортежи, строки) += создаёт новый объект, т.к. в них нет мутации.
  • Для пользовательских классов поведение определяется реализацией __iadd__ и __add__.

Анализ вариантов (кратко)

Представленные варианты вывода:

  • "[1, 2, 3] [1, 2]" — совпадает с реальным результатом: после in-place расширения и последующего создания нового списка для a.
  • Остальные варианты не соответствуют сочетанию in-place изменения и последующего создания нового объекта:

- Оба списка [1,2,3] означали бы, что второе присваивание тоже мутировало исходный объект — это не так.
- Оба [1,2] означали бы, что a не получил [3] после второго шага — неверно.
- Перестановка [1,2] [1,2,3] не имеет смысла, так как b не мог увеличиться до 3, если a был перенаправлен.

Выводы и рекомендации

  • Будьте внимательны с aliasing (несколькими переменными, ссылающимися на один объект). Мутации через одну переменную видны через другие.
  • Используйте .copy() или list() если нужно отдельное независимое копирование: b = a.copy() или b = list(a).
  • При чтении кода помните: += для списков мутирует объект, а выражение a = a + ... создаёт новый объект. Это источник многих тонких ошибок.

Если хотите, могу показать дополнительные примеры с кортежами, строками или пользовательскими классами, чтобы сравнить поведение.

Report Page