Страницы

Поиск по вопросам

суббота, 4 января 2020 г.

TypeError при изменении списка оператором += внутри кортежа

#python


Известно, что оператор += для списков примерно эквивалентен методу extend в том смысле,
что при его использовании не создаётся копия списка, а расширяется текущий.

В связи с этим возникает вопрос: почему следующая конструкция бросает исключение
TypeError:

t = ([1], )  # кортеж из одного элемента, являющегося списком
try:
    t[0] += [2]
except TypeError as e:
    print(e)             # 'tuple' object does not support item assignment
print(t)                 # ([1, 2],) -- и сам список изменился!


Ведь мы не изменяем ссылки, сохранённые в кортеже и следующий код работает без проблем:

t = ([1], )
t[0].extend([2])
print(t)           # ([1, 2],)


Почему такое поведение?
    


Ответы

Ответ 1



Дело в том, что операция += всё же содержит в себе присваивание, хоть и выполняет изменение "на месте". Оператор += реализуется через метод __iadd__, и код t[0] += [2] эквивалентен следующей записи: res = t[0].__iadd__([1]) t[0] = res # TypeError: 'tuple' object does not support item assignment Хоть ссылка на самом деле и не меняется: print(res is t[0]) # True но присваивание есть, и это мешает выполниться подобному оператору без ошибок. Способ с использованием метода, очевидно, такой проблемы иметь не может. Можно удостовериться, что присваивание действительно есть. Для этого рассмотрим более простой случай: a = [1] a += [2] Посмотрим, какой код генерируется в Python 3 для выполнения второй строки (операции могут отличаться, но суть будет примерно та же): import dis dis.dis('a += [2]') Расшифровка действия со стеком 1 0 LOAD_NAME 0 (a) push [1] (переменная "a") 3 LOAD_CONST 0 (2) push 2 6 BUILD_LIST 1 pop (2), push [2] 9 INPLACE_ADD pop ([2]), pop ([1]), push [1, 2] 10 STORE_NAME 0 (a) pop ([1, 2] -> a) ... Именно попытка выполнить операцию STORE_NAME (более точно, STORE_SUBSCR, для кортежа) вызывает ошибку в случае, описанном в вопросе. Важно понимать, что операция INPLACE_ADD выполняет добавление элементов "на месте", т.е. на стеке в итоге окажется тот же объект.

Комментариев нет:

Отправить комментарий