#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 выполняет добавление элементов "на месте", т.е. на стеке в итоге окажется тот же объект.
Комментариев нет:
Отправить комментарий