Зачем нам нужны кортежи в Python (или любой другой тип данных)? - PullRequest
135 голосов
/ 01 февраля 2010

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

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

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

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

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

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

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

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

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

Ответы [ 9 ]

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

  2. в 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
40 голосов
/ 01 февраля 2010

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

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

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

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

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

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

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

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

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


Edit:

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

22 голосов
/ 01 февраля 2010

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

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

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

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

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

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

См. Также « Представляем кортежи », от Dive Into Python .

15 голосов
/ 01 февраля 2010

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

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

9 голосов
/ 01 февраля 2010

Я всегда обнаруживал, что наличие двух совершенно разных типов для одной и той же базовой структуры данных (массивов) является неудобным проектом, но на практике это не является реальной проблемой. (У каждого языка есть свои бородавки, включая 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; Я просто указываю на это, поскольку несколько человек утверждали, что ключи должны быть «неизменяемыми», что является лишь частично правильным. Я использовал это много раз с картами и наборами C ++.

7 голосов
/ 01 февраля 2010

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

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

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

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

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

6 голосов
/ 01 февраля 2010

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

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

Звонящий не имеет гарантии значения a после вызова. Тем не менее,

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

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

1 голос
/ 01 февраля 2010

Ваш вопрос (и последующие комментарии) фокусируется на том, изменяется ли 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

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

Почему это имеет значение? Допустим, у вас есть диктант:

>>> 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'}

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

1 голос
/ 01 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...