Какой тип последовательности лучше для сравнения и почему? (Python) - PullRequest
0 голосов
/ 05 октября 2019

У меня есть условие, которое сравнивает один объект с несколькими другими, например так:

if 'a' in ('a','b','c','e'):

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

1 Ответ

1 голос
/ 05 октября 2019

Всегда используйте набор, пока у вас нет веских причин не делать этого.

Я бы посчитал набор более идиоматичным. Он передает смысл более четко, поскольку порядок не имеет значения, только членство. Если есть сомнения, используйте набор.

И, чтобы быть ясным, набор является коллекцией , но не "типом последовательности" (даже если он повторяется), потому что он семантически "неупорядочен".


Почему бы не использовать набор?

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

Но у кортежей и списков нет этого ограничения.


Почему список над кортежем?

Основным преимуществом списка является просто синтаксическая причуда для одного элемента. Скажем, у вас есть ('foo', 'bar'), а затем решите удалить 'bar'. Тогда у вас есть ('foo'). Ой, посмотри, что я там сделал? На самом деле это должно было быть ('foo',). Запятую легко забыть. А проверка in по-прежнему работает для строк, подобных ('foo'), поскольку in проверяет подстроки. Это может тонко изменить смысл вашей программы. 'oo' находится в ('foo'), но не в ('foo',).

В списке из одного элемента, таком как ['foo'], такой проблемы нет. [И как указал user2357112, список констант все равно будет скомпилирован в кортеж.]

Обратите внимание, что у набора из одного элемента, такого как {'a'}, такой проблемы тоже нет. Пустой {} - это диктат, но это не вызовет проблем с проверкой in, потому что это также пустая коллекция.

Но вы, вероятно, должны использовать == вместо in при сравнении только с одним элементом.


Вот и все для ясности. Теперь о микрооптимизациях. Ранняя оптимизация - корень всего зла. Не оптимизируйте за счет читабельности, пока это на самом деле не понадобится.

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

Насколько мал должен быть сбор, чтобы постоянные издержки набора имели значение? Профиль и посмотри. Производительность может варьироваться в зависимости от множества факторов. Дело не только в количестве элементов, но и в том, сколько времени занимает проверка на равенство, и где они находятся в памяти и т. Д.

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

...