Обновление списка в кортеже

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

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

Я получаю:

TypeError: 'tuple' object does not support item assignment

Это именно то, чего я ожидал. Однако, когда я снова ссылаюсь на кортеж, я получаю:

>>> tup
(1, 2, 3, [4, 5, 6])

Таким образом, список был фактически обновлен, хотя python выбрал исключение. Как это работает? Я не могу представить себе сценарий, в котором я действительно хотел бы сделать что-то подобное, но мне все же хотелось бы понять, что происходит. Спасибо.

Ответ 1

Это фактически документировано в Python docs.

РЕДАКТИРОВАТЬ: здесь резюме, чтобы это был более полный ответ.

  • Когда мы используем +=, Python вызывает магический метод __iadd__ для элемента, затем использует возвращаемое значение в следующем назначении элемента.
  • Для списков __iadd__ эквивалентен вызову extend в списке, а затем возвращает список.
  • Поэтому, когда мы вызываем tup[3] += [6], это эквивалентно:

    result = tup[3].__iadd__([6])
    tup[3] = result
    
  • Из №2 мы можем определить, что это эквивалентно:

    result = tup[3].extend([6])
    tup[3] = result
    
  • Первая строка преуспевает при вызове extend в списке, а так как список изменен, он обновляется. Однако последующее присваивание не выполняется, потому что кортежи неизменяемы и выдает ошибку.