Вы можете всегда изменять изменяемое значение внутри кортежа. Загадочное поведение, которое вы видите с
>>> thing[0] += 'd'
вызвано +=
. Оператор +=
выполняет добавление на месте, но также и назначение - добавление на месте работает только в файле, но назначение завершается ошибкой, поскольку кортеж является неизменным. Думая об этом как
>>> thing[0] = thing[0] + 'd'
объясняет это лучше. Мы можем использовать модуль dis
из стандартной библиотеки, чтобы посмотреть байт-код, сгенерированный из обоих выражений. С +=
мы получаем INPLACE_ADD
байт-код:
>>> def f(some_list):
... some_list += ["foo"]
...
>>> dis.dis(f)
2 0 LOAD_FAST 0 (some_list)
3 LOAD_CONST 1 ('foo')
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (some_list)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
С +
мы получаем BINARY_ADD
:
>>> def g(some_list):
... some_list = some_list + ["foo"]
>>> dis.dis(g)
2 0 LOAD_FAST 0 (some_list)
3 LOAD_CONST 1 ('foo')
6 BUILD_LIST 1
9 BINARY_ADD
10 STORE_FAST 0 (some_list)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
Обратите внимание, что мы получаем STORE_FAST
в обоих местах. Это байт-код, который завершается неудачно при попытке сохранить обратно в кортеж - INPLACE_ADD
, который приходит непосредственно перед тем, как работает нормально.
Это объясняет, почему случай "Не работает и работает" оставляет измененный список позади: кортеж уже имеет ссылку на список:
>>> id(thing[0])
3074072428L
Список затем изменяется INPLACE_ADD
, и STORE_FAST
завершается ошибкой:
>>> thing[0] += 'd'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Таким образом, кортеж все еще имеет ссылку на тот же список , но список был изменен на месте:
>>> id(thing[0])
3074072428L
>>> thing[0]
['b', 'c', 'd']