Подобные «расширенные» операторы присваивания +=были введены в 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])после выполнения строки, которая вызывает ошибку.