«Расширенные» операторы присваивания, такие как +=
, были введены в 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
singleton, в этом случае поиск продолжается со следующего шага).
Этот процесс допускает типы, которые поддерживаютмодификация на месте для реализации __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])
после выполнения строки, которая вызывает ошибку.