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

Добавить в список, определенный в кортеже - это ошибка?

Итак, у меня есть этот код:

tup = ([1,2,3],[7,8,9])
tup[0] += (4,5,6)

который генерирует эту ошибку:

TypeError: 'tuple' object does not support item assignment

Пока этот код:

tup = ([1,2,3],[7,8,9])
try:
    tup[0] += (4,5,6)
except TypeError:
    print tup

печатает это:

([1, 2, 3, 4, 5, 6], [7, 8, 9])

Ожидается ли такое поведение?

Примечание

Я понимаю, что это не очень распространенный случай использования. Однако, хотя ожидается ошибка, я не ожидал изменения списка.

4b9b3361

Ответ 1

Да, он ожидал.

Кортеж не может быть изменен. Кортеж, как и список, представляет собой структуру, которая указывает на другие объекты. Он не заботится о том, что эти объекты. Они могут быть строками, числами, кортежами, списками или другими объектами.

Так что делайте что-либо одному из объектов, содержащихся в кортеже, включая добавление к этому объекту, если это список, не относится к семантике кортежа.

(Представьте, если вы написали класс, на котором были методы, из-за которых его внутреннее состояние изменилось. Вы не ожидали, что невозможно будет вызвать эти методы для объекта на основе того, где он хранится).

Или другой пример:

>>> l1 = [1, 2, 3]
>>> l2 = [4, 5, 6]
>>> t = (l1, l2)
>>> l3 = [l1, l2]
>>> l3[1].append(7)

Два изменяемых списка, на которые ссылается список и кортеж. Должна ли я выполнить последнюю строку (ответ: да). Если вы думаете, что ответ нет, почему бы и нет? Если t изменить семантику l3 (ответ: нет).

Если вы хотите неизменный объект последовательных структур, он должен быть кортежами до конца.

Почему это ошибка?

В этом примере используется оператор infix:

Многие операции имеют "на месте" версию. Следующие функции обеспечивают более примитивный доступ к операторам на месте, чем обычные синтаксис does; например, утверждение x + = y эквивалентно x = operator.iadd(x, y). Другой способ сказать, что z = оператор .iadd(x, y) эквивалентен составному утверждению z = x; Z + = y.

https://docs.python.org/2/library/operator.html

Итак, это:

l = [1, 2, 3]
tup = (l,)
tup[0] += (4,5,6)

эквивалентно этому:

l = [1, 2, 3]
tup = (l,)
x = tup[0]
x = x.__iadd__([4, 5, 6]) # like extend, but returns x instead of None
tup[0] = x

Линия __iadd__ преуспевает и изменяет первый список. Таким образом, список был изменен. Вызов __iadd__ возвращает измененный список.

Вторая строка пытается назначить список кортежу, и это не удается.

Итак, в конце программы список был расширен, но вторая часть операции += не удалась. Подробные сведения см. В этом вопросе.

Ответ 2

Ну, я думаю, tup[0] += (4, 5, 6) переводится на:

tup[0] = tup[0].__iadd__((4,5,6))

tup[0].__iadd__((4,5,6)) выполняется нормально, изменяя список в первом элементе. Но назначение терпит неудачу, поскольку кортежи являются неизменяемыми.

Ответ 3

Корреспонденты не могут быть изменены напрямую, исправлены. Тем не менее, вы можете изменить элемент кортежа по ссылке. Как:

>>> tup = ([1,2,3],[7,8,9])
>>> l = tup[0]
>>> l += (4,5,6)
>>> tup
([1, 2, 3, 4, 5, 6], [7, 8, 9])

Ответ 4

Разработчики 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 терпит неудачу, потому что вы не можете положить материал в кортежи после их создания (кортежи неизменяемы).

В реальной программе я предлагаю вам не делать предложение try-except, потому что tup[0].extend([4,5,6]) делает то же самое.