Подобные «расширенные» операторы присваивания +=
были введены в Python 2.0, который был выпущен в октябре 2000 года. Дизайн и обоснование описаны в PEP 203 . Одной из заявленных целей этих операторов была поддержка операций на месте. Письмо
a = [1, 2, 3]
a += [4, 5, 6]
предполагается обновить список a
на месте . Это имеет значение, если есть другие ссылки на список a
, например, когда a
был получен в качестве аргумента функции.
Однако операция не всегда может происходить на месте, так как многие типы Python, включая целые числа и строки, являются неизменяемыми , поэтому, например, i += 1
для целого числа i
не может работать на месте.
Таким образом, операторы расширенного присваивания должны были работать, когда это возможно, и создавать новый объект в противном случае. Для облегчения этих целей проектирования выражение x += y
было задано следующим образом:
- Если
x.__iadd__
определено, x.__iadd__(y)
оценивается.
- В противном случае, если
x.__add__
реализуется x.__add__(y)
, оценивается.
- В противном случае, если
y.__radd__
реализуется y.__radd__(x)
, оценивается.
- В противном случае выведите ошибку.
Первый результат, полученный этим процессом, будет возвращен x
(если только этот результат не является NotImplemented
одиночным, в этом случае поиск продолжается со следующего шага).
Этот процесс позволяет реализовывать типы, которые поддерживают модификацию на месте __iadd__()
. Типы, которые не поддерживают модификацию на месте, не должны добавлять какие-либо новые магические методы, так как Python автоматически вернется к существенному x = x + y
.
Итак, давайте наконец подойдем к вашему актуальному вопросу - почему вы можете добавить кортеж в список с помощью оператора расширенного присваивания. По памяти история этого была примерно такой: list.__iadd__()
метод был реализован для простого вызова уже существующего list.extend()
метода в Python 2.0. Когда в Python 2.1 были представлены итераторы, list.extend()
метод был обновлен, чтобы принимать произвольные итераторы. Конечным результатом этих изменений было то, что my_list += my_tuple
работало начиная с Python 2.1. Однако list.__add__()
метод никогда не должен был поддерживать произвольные итераторы в качестве правого аргумента - это считалось неуместным для строго типизированного языка.
Лично я считаю, что реализация расширенных операторов оказалась слишком сложной в Python. У него много неожиданных побочных эффектов, например, этот код:
t = ([42], [43])
t[0] += [44]
Возникает вторая строка TypeError: 'tuple' object does not support item assignment
, но операция все равно успешно выполняется - t
будет ([42, 44], [43])
после выполнения строки, которая вызывает ошибку.