Почему кортежи могут содержать изменяемые элементы?


178

Если кортеж является неизменным, то почему он может содержать изменяемые элементы?

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

Ответы:


205

Это отличный вопрос.

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

Другое понимание состоит в том, что контейнеры Python на самом деле ничего не содержат. Вместо этого они хранят ссылки на другие объекты. Аналогично, переменные Python не похожи на переменные в скомпилированных языках; вместо этого имена переменных - это просто ключи в словаре пространства имен, где они связаны с соответствующим объектом. Нед Батчелдер хорошо объясняет это в своем блоге . В любом случае, объекты знают только свой счетчик ссылок; они не знают, что это за ссылки (переменные, контейнеры или внутренние компоненты Python).

Вместе эти две идеи объясняют вашу загадку (почему неизменный кортеж, «содержащий» список, кажется, изменяется при изменении базового списка). Фактически, кортеж не изменился (он по-прежнему имеет те же ссылки на другие объекты, что и раньше). Кортеж не мог измениться (потому что у него не было методов мутации). Когда список изменился, кортеж не получил уведомление об изменении (список не знает, ссылается ли он на переменную, кортеж или другой список).

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

  1. Кортежи характеризуются в меньшей степени своей неизменностью, а в большей степени - их назначением.
    Кортежи - это способ Python собирать разнородную информацию под одной крышей. Например, s = ('www.python.org', 80) объединяет строку и число, чтобы пара хост / порт могла передаваться как сокет, составной объект. С этой точки зрения вполне разумно иметь изменяемые компоненты.

  2. Неизменность идет рука об руку с другим свойством, хешабильностью . Но hashability не является абсолютным свойством. Если один из компонентов кортежа не может быть хэшируемым, то и весь кортеж не может быть хэшируемым. Например, t = ('red', [10, 20, 30])не может быть хэшируемым.

Последний пример показывает 2-кортеж, который содержит строку и список. Сам кортеж не является изменяемым (т. Е. У него нет методов для изменения его содержимого). Аналогично, строка является неизменной, потому что строки не имеют никаких методов мутации. У объекта списка есть методы мутации, поэтому его можно изменить. Это показывает, что изменчивость является свойством типа объекта - некоторые объекты имеют методы мутации, а некоторые - нет. Это не меняется только потому, что объекты вложены.

Помните две вещи. Во-первых, неизменность - это не волшебство, а просто отсутствие методов мутации. Во-вторых, объекты не знают, какие переменные или контейнеры ссылаются на них - они знают только количество ссылок.

Надеюсь, это было полезно для вас :-)


Не правда ли, «у кортежей нет возможности узнать, являются ли объекты внутри них изменчивыми». Мы можем определить, реализует ли ссылка хеш-метод, тогда он неизменен. Как бы сделал диск или сет. Разве это не было бы больше дизайнерским решением для того, какими должны быть кортежи?
garg10may

@ garg10may 1) Хеш-хэнд не легко обнаружить без вызова, hash()потому что все, что наследуется от объекта () является хэшируемым, и поэтому подклассы должны явно отключить хеширование. 2) Hashibility не гарантирует неизменность - легко создать примеры изменяемых объектов. 3) Кортежи, как и большинство контейнеров в Python, просто имеют ссылки на базовый объект - они не обязаны проверять их и делать выводы о них.
Рэймонд Хеттингер

171

Это потому, что кортежи не содержат списков, строк или чисел. Они содержат ссылки на другие объекты . 1 Невозможность изменить последовательность ссылок, содержащихся в кортеже, не означает, что вы не можете изменить объекты, связанные с этими ссылками. 2

1. Объекты, значения и типы (см .: абзацы от второго до последнего)
2. Стандартная иерархия типов (см .: «Неизменяемые последовательности»)


8
В этом вопросе есть двусмысленность. Этот ответ полностью объясняет , почему это возможно для кортежей содержат изменяемые объекты. Это не объясняет, почему кортежи были разработаны, чтобы содержать изменяемые объекты. Я думаю, что последний является более актуальным вопросом.
senderle

16

Прежде всего, слово «неизменный» может означать много разных вещей для разных людей. Мне особенно нравится, как Эрик Липперт классифицировал неизменность в своем блоге . Там он перечисляет такие виды неизменности:

  • Realio-Trulio неизменность
  • Неизменяемость при записи
  • Неизменность эскимо
  • Мелкий против глубокой неизменности
  • Неизменяемые фасады
  • Наблюдательная неизменность

Их можно комбинировать различными способами, чтобы сделать еще больше видов неизменности, и я уверен, что их будет больше. Вид неизменности, который вас интересует, представляет собой глубокую (также известную как переходная) неизменность, в которой неизменяемые объекты могут содержать только другие неизменные объекты.

Ключевым моментом этого является то, что глубокая неизменность является лишь одним из многих, многих видов неизменности. Вы можете принять тот тип, который предпочитаете, если вы знаете, что ваше понятие «неизменного», вероятно, отличается от чьего-либо другого понятия «неизменного».


Какой тип неизменности есть у кортежей Python?
qazwsx

3
Кортежи Python имеют мелкую (иначе говоря, нетранзитивную) неизменность.
Кен Уэйн ВандерЛинде

16

Насколько я понимаю, этот вопрос необходимо перефразировать как вопрос о проектных решениях: почему разработчики Python решили создать тип неизменяемой последовательности, который может содержать изменяемые объекты?

Для того, чтобы ответить на этот вопрос, мы должны думать о цели кортежи служить: они служат в качестве быстрого , общего назначения последовательности. Имея это в виду, становится совершенно очевидно, почему кортежи являются неизменяемыми, но могут содержать изменяемые объекты. Для остроумия:

  1. Кортежи быстры и эффективны с точки зрения памяти: кортежи создаются быстрее, чем списки, потому что они неизменяемы. Неизменяемость означает, что кортежи можно создавать как константы и загружать как таковые, используя постоянное свертывание . Это также означает, что они быстрее и эффективнее для создания памяти, потому что нет необходимости в перераспределении и т. Д. Они немного медленнее, чем списки для произвольного доступа к элементам, но быстрее для распаковки (по крайней мере, на моем компьютере). Если бы кортежи были изменяемыми, они бы не были такими быстрыми для таких целей, как эти.

  2. Кортежи общего назначения : кортежи должны содержать любой объект. Они привыкли (быстро) делать такие вещи, как списки аргументов переменной длины (через *оператор в определениях функций). Если кортежи не могут содержать изменяемые объекты, они будут бесполезны для подобных вещей. Python должен был бы использовать списки, которые, вероятно, замедляли бы работу и, конечно, были бы менее эффективными в использовании памяти.

Итак, вы видите, что для выполнения своей цели кортежи должны быть неизменяемыми, но также должны иметь возможность содержать изменяемые объекты. Если бы разработчики Python хотели создать неизменяемый объект, который гарантирует, что все объекты, которые он «содержит», также являются неизменяемыми, им пришлось бы создать третий тип последовательности. Усиление не стоит дополнительной сложности.


12

Вы не можете изменить idего элементы. Так что он всегда будет содержать одни и те же предметы.

$ python
>>> t = (1, [2, 3])
>>> id(t[1])
12371368
>>> t[1].append(4)
>>> id(t[1])
12371368

Это наиболее подходящая демонстрация приведенных выше примеров. У кортежа есть ссылки на те объекты, которые не меняются, хотя наличие не более одного изменяемого компонента делает весь кортеж не подлежащим изменению.
piepi

5

Здесь я остановлюсь на конечности и скажу, что важная часть здесь заключается в том, что, хотя вы можете изменить содержимое списка или состояние объекта, содержащегося в кортеже, вы не можете изменить то, что объект или список есть. Если бы у вас было что-то, что зависело от того, что [3] было списком, даже если оно пустое, я мог бы посчитать это полезным.


3

Одна из причин заключается в том, что в Python нет общего способа преобразовать изменяемый тип в неизменяемый (см. Отклоненный PEP 351 и связанную с ним дискуссию почему он был отклонен). Таким образом, было бы невозможно помещать различные типы объектов в кортежи, если бы у него было это ограничение, включая практически любой созданный пользователем объект без хэша.

Единственная причина, по которой словари и наборы имеют это ограничение, заключается в том, что они требуют, чтобы объекты были хэшируемыми, поскольку они реализованы внутри как хеш-таблицы. Но обратите внимание , что, по иронии судьбы, словари и сами наборы не неизменны (или hashable). Кортежи не используют хэш объекта, поэтому его изменчивость не имеет значения.


2

Кортеж является неизменным в том смысле, что сам кортеж не может расширяться или сжиматься, а не то, что все содержащиеся в нем элементы являются неизменяемыми. В противном случае кортежи скучны.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.