Подтвердить что ты не робот

Кортежи: + = оператор выдает исключение, но преуспевает?

Почему следующие исключения вызывают исключение, хотя это удается?

>>> t = ([1, 2, 3], 4)
>>> t[0] += [1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
([1, 2, 3, 1], 4)
>>> 
4b9b3361

Ответ 1

Найден ответ на IRC.

t[0] += [1] - несколько дискретных действий:

  • Загрузка t[0]
  • создание нового списка с 1 в нем
  • добавив, что [1] для t[0]
  • переназначение t[0]

Кажется, что x += y в основном x = x + y (но, это?)

Сложный бит заключается в том, что += подразумевает назначение как кортежа t, так и списка t[0]

t[0] += [1] не буквально t[0] = t[0] + [1], это: t[0] = t[0].__iadd__([1])

Что действительно происходит:

  • __iadd__ оба мутируют список и возвращают его. Таким образом, список (который является первым элементом в t) уже добавил к нему 1.
  • мутация типа tuple также выполняется на месте, но кортежи неизменяемы, что приводит к исключению.

Почему это не видно на виду? Поскольку n00b, подобный мне, ожидал бы, что t[0] += [1] либо сработает вместе, либо потерпит неудачу, потому что это одна короткая строка python. Но это не всегда так.

Ответ 2

Это также поможет понять это поведение, взглянув на байт-код с dis.dis.

In[5]: dis('t[0] += [1]')
  1           0 LOAD_NAME                0 (t)
              3 LOAD_CONST               0 (0)
              6 DUP_TOP_TWO
              7 BINARY_SUBSCR
              8 LOAD_CONST               1 (1)
             11 BUILD_LIST               1
             14 INPLACE_ADD
             15 ROT_THREE
             16 STORE_SUBSCR
             17 LOAD_CONST               2 (None)
             20 RETURN_VALUE
  • Значение t[0] помещается поверх стека с BINARY_SUBSCR, который является (изменяемым) списком в этом случае.
  • Значение в верхней части стека имеет += [1], выполняемое на нем с INPLACE_ADD, где в этом случае верхняя часть стека относится к списку внутри кортежа.
  • Назначение t[0] в начало стека происходит с помощью STORE_SUBSCR, который здесь не выполняется, поскольку t сам является неизменяемым кортежем, что вызывает ошибку после того, как назначение += уже произошло.

Ответ 3

Разработчики Python опубликовали официальное объяснение, почему это происходит здесь: https://docs.python.org/2/faq/programming.html#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works

Короткий вариант состоит в том, что + = фактически выполняет две вещи: одну за другой:

  • Возьмите вещь справа и добавьте ее в переменную слева
  • Поместите результат в переменную слева

В этом случае шаг 1 работает, потому что вам разрешено добавлять материал в списки (theyre mutable), но шаг 2 терпит неудачу, потому что вы не можете положить материал в кортежи после их создания (кортежи неизменяемы).

В реальной программе я предлагаю вам не делать этого, потому что t [0].extend(['c']) делает то же самое.