Зачем нам нужны кортежи в Python (или любой неизменяемый тип данных)?

Я прочитал несколько руководств по python (Dive Into Python, для одного) и ссылку на язык на Python.org - я не понимаю, зачем языку нужны кортежи.

Кортежи не имеют методов по сравнению с списком или набором, и если я должен преобразовать кортеж в набор или список, чтобы иметь возможность их сортировать, то в чем смысл использования кортежа в первую очередь?

Неизменность?

Почему кому-то интересно, если переменная живет в другом месте в памяти, чем когда она была первоначально выделена? Все это дело неизменности в Python, кажется, было подчеркнуто.

В C/С++, если я выделяю указатель и указываю на некоторую допустимую память, мне все равно, где находится адрес, если он не равен null, прежде чем я его использую.

Всякий раз, когда я ссылаюсь на эту переменную, мне не нужно знать, указывает ли указатель на исходный адрес или нет. Я просто проверяю значение null и использую его (или нет).

В Python, когда я выделяю строку (или кортеж), назначаю ее x, а затем изменяю строку, почему мне все равно, является ли это исходным объектом? Пока переменная указывает на мои данные, все, что имеет значение.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

x все еще ссылается на данные, которые я хочу, почему кто-то должен заботиться о том, является ли его идентификатор одинаковым или другим?

Ответ 1

  • неизменяемые объекты могут позволить существенную оптимизацию; это, по-видимому, то, что строки также неизменны в Java, разработанные совершенно отдельно, но примерно в то же время, что и Python, и почти все неизменено на действительно функциональных языках.

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

Пример проблемы оптимизации:

$ python -mtimeit '["fee", "fie", "fo", "fum"]'
1000000 loops, best of 3: 0.432 usec per loop
$ python -mtimeit '("fee", "fie", "fo", "fum")'
10000000 loops, best of 3: 0.0563 usec per loop

Ответ 2

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

Кортежи и списки служат для разных целей. Списки хранят однородные данные. У вас может и должен быть такой список:

["Bob", "Joe", "John", "Sam"]

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

["Billy", "Bob", "Joe", 42]

В этом списке указано полное имя одного человека и его возраст. Это не один тип данных. Правильный способ хранения этой информации либо в кортеже, либо в объекте. Допустим, у нас есть несколько:

[("Billy", "Bob", "Joe", 42), ("Robert", "", "Smith", 31)]

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

Пожалуйста, не делайте этого.


Edit:

Я думаю, что в этом блоге объясняется, почему я думаю, что это лучше, чем я: http://news.e-scribe.com/397

Ответ 3

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

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

Как вы указываете, кортежи неизменяемы. Причины наличия неизменяемых типов относятся к кортежам:

  • Эффективность копирования: вместо копирования неизменяемого объекта вы можете его псевдонимом (привязать переменную к ссылке)
  • Эффективность сравнения: при использовании копирования по ссылке вы можете сравнить две переменные путем сравнения местоположения, а не содержимого
  • интернирование: вам нужно хранить не более одной копии любого неизменяемого значения
  • нет необходимости синхронизировать доступ к неизменяемым объектам в параллельном коде
  • const correctness: некоторые значения не должны меняться. Это (для меня) является основной причиной неизменяемых типов.

Обратите внимание, что конкретная реализация Python может не использовать все перечисленные выше функции.

Словарные ключи должны быть неизменными, иначе изменение свойств ключевого объекта может привести к недействительности инвариантов базовой структуры данных. Таким образом, кортежи могут быть использованы в качестве ключей. Это является следствием константной корректности.

См. также " Представление кортежей", Погружение в Python.

Ответ 4

Иногда нам нравится использовать объекты в качестве словарных клавиш

Для чего стоит, кортежи в последнее время (2.6+) выросли index() и count() методы

Ответ 5

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

Почему кому-то интересно, если переменная живет в другом месте в памяти, чем когда она была первоначально выделена? Все это дело неизменности в Python, кажется, было подчеркнуто.

Это разные вещи. Мутируемость не связана с местом, которое оно хранит в памяти; это означает, что материал, на который он указывает, не может измениться.

Объекты Python не могут изменять местоположение после их создания, изменяемые или нет. (Точнее, значение id() не может измениться - то же самое, на практике.) Внутреннее хранилище изменяемых объектов может измениться, но это скрытая информация о реализации.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

Это не изменяет ( "мутирует" ) переменную; он создает новую переменную с тем же именем и отбрасывает старую. Сравните с мутирующей операцией:

>>> a = [1,2,3]
>>> id(a)
3084599212L
>>> a[1] = 5
>>> a
[1, 5, 3]
>>> id(a)
3084599212L

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

Обратите внимание, что ключи для словарей не обязательно должны быть полностью неизменными. Только часть его, используемая в качестве ключа, должна быть неизменной; для некоторых целей это важное различие. Например, у вас может быть класс, представляющий пользователя, который сравнивает равенство и хэш по уникальному имени пользователя. Затем вы можете вставлять другие изменчивые данные в класс - "пользователь вошел в систему" ​​и т.д. Так как это не влияет на равенство или хэш, возможно и совершенно правильно использовать это как ключ в словаре. Это не слишком часто необходимо в Python; Я просто указываю на это, поскольку несколько человек заявили, что ключи должны быть "неизменными", что является лишь частично правильным. Я использовал это много раз с картами и наборами С++.

Ответ 6

Как gnibbler предложил в комментарии, Guido имел мнение, которое не полностью признано/оценено: "списки для однородных данных, кортежи - это для гетерогенных данных". Конечно, многие из противников интерпретировали это как означающее, что все элементы списка должны быть одного типа.

Мне нравится видеть это по-другому, в отличие от других, которые были в прошлом:

blue= 0, 0, 255
alist= ["red", "green", blue]

Обратите внимание, что я считаю, что alist является однородным, даже если type (alist [1])!= type (alist [2]).

Если я могу изменить порядок элементов, и у меня не будет проблем в моем коде (кроме предположений, например, "он должен быть отсортирован" ), тогда следует использовать список. Если нет (например, в кортеже blue выше), то я должен использовать кортеж.

Ответ 7

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

a = [1,1,1]
doWork(a)

После вызова вызывающий абонент не может гарантировать значение a. Однако

a = (1,1,1)
doWorK(a)

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

Ответ 8

вы можете увидеть здесь для обсуждения этого

Ответ 9

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

Прежде чем продолжить, убедитесь, что приведенное ниже поведение - это то, что вы ожидаете от Python.

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1[0] = 2
>>> print a2[0]
2

В этом случае содержимое a2 было изменено, хотя только a1 присвоило новое значение. Сравните со следующим:

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1 = [2]
>>> print a2[0]
1

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

Почему это имеет значение? Скажем, у вас есть dict:

>>> t1 = (1,2)
>>> d1 = { t1 : 'three' }
>>> print d1
{(1,2): 'three'}
>>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
>>> t1 = (2,3) ## creates a new tuple, does not modify the old one
>>> print d1   ## as seen here, the dict is still intact
{(1,2): 'three'}

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