Это отличный вопрос.
Ключевое понимание состоит в том, что у кортежей нет возможности узнать, являются ли объекты внутри них изменчивыми. Единственное, что делает объект изменчивым, - это наличие метода, который изменяет его данные. В общем, нет способа обнаружить это.
Другое понимание состоит в том, что контейнеры Python на самом деле ничего не содержат. Вместо этого они хранят ссылки на другие объекты. Аналогично, переменные Python не похожи на переменные в скомпилированных языках; вместо этого имена переменных - это просто ключи в словаре пространства имен, где они связаны с соответствующим объектом. Нед Батчелдер хорошо объясняет это в своем блоге . В любом случае, объекты знают только свой счетчик ссылок; они не знают, что это за ссылки (переменные, контейнеры или внутренние компоненты Python).
Вместе эти две идеи объясняют вашу загадку (почему неизменный кортеж, «содержащий» список, кажется, меняется при изменении базового списка). Фактически, кортеж не изменился (он по-прежнему имеет те же ссылки на другие объекты, что и раньше). Кортеж не мог измениться (потому что у него не было методов мутации). Когда список изменился, кортеж не получил уведомление об изменении (список не знает, ссылается ли он на переменную, кортеж или другой список).
Пока мы размышляем над этой темой, вот несколько других мыслей, которые помогут вам завершить вашу ментальную модель того, что такое кортежи, как они работают, и их предполагаемое использование:
Кортежи характеризуются в меньшей степени своей неизменностью, а в большей степени - по назначению.
Кортежи - это способ Python собирать разнородную информацию под одной крышей. Например,
s = ('www.python.org', 80)
объединяет строку и число, так что пара хост / порт может передаваться как сокет, составной объект. С этой точки зрения вполне разумно иметь изменяемые компоненты.
Неизменяемость идет рука об руку с другим свойством, hashability . Но hashability не является абсолютным свойством. Если один из компонентов кортежа не является хэшируемым, то и весь кортеж не может быть хэшируемым. Например, t = ('red', [10, 20, 30])
не является хэшируемым.
Последний пример показывает 2-кортеж, который содержит строку и список. Сам кортеж не является изменяемым (т. Е. У него нет методов для изменения его содержимого). Аналогично, строка является неизменной, потому что строки не имеют никаких методов мутации. У объекта списка есть методы мутации, поэтому его можно изменить. Это показывает, что изменчивость является свойством типа объекта - некоторые объекты имеют методы мутации, а некоторые нет. Это не изменится только потому, что объекты вложены.
Помните две вещи. Во-первых, неизменность - это не магия, а просто отсутствие методов мутации. Во-вторых, объекты не знают, какие переменные или контейнеры ссылаются на них - они знают только количество ссылок.
Надеюсь, это было полезно для вас: -)