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

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

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

Ответ 1

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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