← Назад к вопросам

Что будет если к списку находящемуся в кортеже через оператор += присвоить список?

1.0 Junior🔥 171 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Список в кортеже и оператор +=

Это классический вопрос, который показывает, что разработчик путает mutability и immutability в Python. Ответ — интересный и парадоксальный.

Что произойдёт

# Создаём кортеж со списком
tup = ([1, 2, 3], "hello")
print(tup)  # ([1, 2, 3], 'hello')

# Используем +=
tup[0] += [4, 5]

# Результат: TypeError!
# TypeError: 'tuple' object does not support item assignment

Но вот что интересно...

# Хотя произойдёт ошибка...
tup = ([1, 2, 3], "hello")

try:
    tup[0] += [4, 5]  # Ошибка!
except TypeError as e:
    print(f"Error: {e}")

# ...но список ИЗМЕНИЛСЯ!
print(tup)  # ([1, 2, 3, 4, 5], 'hello')
print(tup[0])  # [1, 2, 3, 4, 5] <- Изменён!

Почему это происходит?

Ключ — в том, как Python обрабатывает +=:

# a += b эквивалентно:
# 1. temp = a.__iadd__(b)  <- in-place operation
# 2. a = temp             <- assignment

# Для списков __iadd__ ИЗМЕНЯЕТ список in-place:
tup[0] += [4, 5]

# Это примерно то же самое, что:
temp = tup[0].__iadd__([4, 5])  # Список изменяется!
assignment = tup[0] = temp  # А вот тут происходит ошибка!

Детальный разбор

Шаг 1: In-place операция происходит:

tup = ([1, 2, 3], "hello")

# Список в памяти изменяется
tup[0].__iadd__([4, 5])  # Работает! Список изменился
print(tup[0])  # [1, 2, 3, 4, 5]

Шаг 2: Assignment падает:

# Потом Python пытается переassign элемент кортежа
tup[0] = temp  # TypeError: 'tuple' object does not support item assignment
# Кортежи immutable!

Практический пример

class DemonstrationOfTheIssue:
    def __init__(self):
        self.lst = [1, 2, 3]
        self.tup = (self.lst, "hello")
    
    def show_the_problem(self):
        print(f"Before: {self.tup}")
        print(f"List id: {id(self.tup[0])}")
        
        try:
            self.tup[0] += [4, 5]
        except TypeError as e:
            print(f"\nGot error: {e}")
        
        # НО СПИСОК ИЗМЕНИЛСЯ!
        print(f"\nAfter: {self.tup}")
        print(f"List id: {id(self.tup[0])}")
        # id одинаковый — это ТОТ ЖЕ список!

demo = DemonstrationOfTheIssue()
demo.show_the_problem()

# Output:
# Before: ([1, 2, 3], 'hello')
# List id: 140234567890000
# 
# Got error: 'tuple' object does not support item assignment
# 
# After: ([1, 2, 3, 4, 5], 'hello')
# List id: 140234567890000  <- Тот же объект!

Почему это важно понимать

# Это создаёт неожиданное поведение
data = ([1, 2, 3], "info")

# Кажется, что это не сработает...
try:
    data[0] += [4, 5]
except TypeError:
    pass  # Ошибка!

# ...но данные изменились!
assert data[0] == [1, 2, 3, 4, 5]  # True!

# Это может привести к трудноуловимым багам:
original = ([1, 2], "name")
backup = original  # Кортежи типа "pass by reference"

try:
    original[0] += [3, 4]
except TypeError:
    pass

print(backup[0])  # [1, 2, 3, 4] <- Изменился и backup!
# Потому что backup указывает на ТОТ ЖЕ кортеж и ТОТ ЖЕ список

Как это происходит на уровне bytecode

import dis

def modify_tuple():
    tup = ([1, 2, 3], "hello")
    tup[0] += [4, 5]

dis.dis(modify_tuple)

# Результат (упрощённо):
# LOAD_NAME тup
# LOAD_CONST 0  (индекс 0)
# DUP_TOP_TWO
# BINARY_SUBSCR  <- Получаем список
# LOAD_NAME [4, 5]
# INPLACE_ADD  <- Изменяем список in-place!
# ROT_TWO
# STORE_SUBSCR  <- Пытаемся переassign... ОШИБКА!

Правильные способы работы

1. Изменять список напрямую (правильно):

tup = ([1, 2, 3], "hello")
tup[0].extend([4, 5])  # Работает!
print(tup)  # ([1, 2, 3, 4, 5], 'hello')

2. Использовать список, а не кортеж:

lst = [[1, 2, 3], "hello"]
lst[0] += [4, 5]  # Работает!
print(lst)  # ([1, 2, 3, 4, 5], 'hello')

3. Если нужна immutability, использовать immutable структуры:

from typing import Tuple

# Правильно: кортеж с immutable данными
tup: Tuple[tuple, str] = ((1, 2, 3), "hello")
# tup[0] += (4, 5)  # Это создаст новый кортеж, не ошибку
new_tup = (tup[0] + (4, 5), tup[1])
print(new_tup)  # ((1, 2, 3, 4, 5), 'hello')

Вывод

Главное правило:

  • Кортежи immutable — нельзя менять элементы
  • Но объекты внутри кортежа могут быть mutable — их можно менять
  • += для списка = __iadd__ (изменение in-place) + assignment
  • Изменение происходит, assignment падает → парадокс

Это отличный пример, почему важно знать разницу между mutable и immutable, и как Python обрабатывает операции на уровне bytecode.

Что будет если к списку находящемуся в кортеже через оператор += присвоить список? | PrepBro