Может ли в кортеж быть вложен список в Python
Что проверяют
Понимаете ли вы, что неизменяемость кортежа поверхностная: зафиксированы ссылки, а не объекты, на которые они указывают. Вопрос простой, поэтому ценятся следствия: хэшируемость и ловушка с +=. Следом обычно дают код t[1] += [3] и просят предсказать результат.
Как отвечать
Да, может. Кортеж хранит ссылки на любые объекты, и запрещено только переназначить элемент: t[0] = 10 даст TypeError. Сам список внутри остаётся обычным изменяемым объектом, поэтому t = (1, [2]); t[1].append(3) отработает, и t станет (1, [2, 3]).
Дальше покажите, что из этого следует:
- хэшируемость: кортеж хэшируется, только если хэшируются все его элементы.
hash((1, [2]))падает сTypeError: unhashable type: 'list', значит такой кортеж не станет ключом словаря, элементомsetи аргументом функции подfunctools.lru_cache; - ловушка
+=:t[1] += [3]сначала вызываетlist.__iadd__и расширяет список на месте, а потом пытается записать результат обратно вt[1]. Итог: исключениеTypeError, но список уже изменён. Этот случай разобран в официальном FAQ Python, и его любят спрашивать; - копирование:
copy.copy(t)для кортежа возвращает тот же объект, и список в копии общий. Независимую копию даётcopy.deepcopy; - то же у родственников:
NamedTupleи@dataclass(frozen=True)запрещают переназначать поля, но не защищают вложенный список отappend.
Если нужна настоящая неизменяемость, внутрь кладут неизменяемые типы: кортеж вместо списка, frozenset вместо множества.
Где спотыкаются
Отвечают «нет, кортеж неизменяемый, значит и содержимое тоже». Это путаница между неизменяемостью контейнера и неизменяемостью объектов в нём.
На задаче с t[1] += [3] выбирают один из двух исходов: либо «ошибка, ничего не поменялось», либо «всё сработает». На деле происходит и то, и другое.
Используют кортеж как защиту данных, например возвращают его из модуля как «константу». Вызывающий код спокойно меняет вложенный список, и общий объект портится у всех, кто его держит.