Python имеет упорядоченный словарь . Как насчет заказанного набора?
collections.Counter
- сумка Питона.
Python имеет упорядоченный словарь . Как насчет заказанного набора?
collections.Counter
- сумка Питона.
Ответы:
Есть заказанный набор (возможна новая ссылка рецепт ), на который ссылается Документация Python 2 . Это работает на Py2.6 или позже и 3.0 или позже без каких-либо изменений. Интерфейс почти такой же, как обычный набор, за исключением того, что инициализация должна быть сделана со списком.
OrderedSet([1, 2, 3])
Это MutableSet, поэтому подпись для .union
не совпадает с сигнатурой набора, но поскольку она включает в себя __or__
нечто подобное, можно легко добавить:
@staticmethod
def union(*sets):
union = OrderedSet()
union.union(*sets)
return union
def union(self, *sets):
for set in sets:
self |= set
update
, union
, intersection
.
union
в одном и том же классе. Последний «победит», а первый не сможет существовать во время выполнения. Это потому, что OrderedSet.union
(без паренов) должен ссылаться на один объект.
Ключи словаря являются уникальными. Таким образом, если игнорировать значения в упорядоченном словаре (например, назначая ихNone
), то он, по сути, имеет упорядоченный набор.
Начиная с Python 3.1 есть collections.OrderedDict
. Ниже приведен пример реализации OrderedSet. (Обратите внимание, что только несколько методов должны быть определены или переопределены: collections.OrderedDict
и collections.MutableSet
выполнять тяжелую работу.)
import collections
class OrderedSet(collections.OrderedDict, collections.MutableSet):
def update(self, *args, **kwargs):
if kwargs:
raise TypeError("update() takes no keyword arguments")
for s in args:
for e in s:
self.add(e)
def add(self, elem):
self[elem] = None
def discard(self, elem):
self.pop(elem, None)
def __le__(self, other):
return all(e in other for e in self)
def __lt__(self, other):
return self <= other and self != other
def __ge__(self, other):
return all(e in self for e in other)
def __gt__(self, other):
return self >= other and self != other
def __repr__(self):
return 'OrderedSet([%s])' % (', '.join(map(repr, self.keys())))
def __str__(self):
return '{%s}' % (', '.join(map(repr, self.keys())))
difference = __sub__
difference_update = __isub__
intersection = __and__
intersection_update = __iand__
issubset = __le__
issuperset = __ge__
symmetric_difference = __xor__
symmetric_difference_update = __ixor__
union = __or__
OrderedSet
какие подклассы OrderedDict
и abc.Set
затем определить __len__
, __iter__
и __contains__
.
collections
, но в остальном хорошее предложение
OrderedSet([1,2,3])
возникает ошибка TypeError. Как конструктор вообще работает? Отсутствующий пример использования.
Ответ - нет, но вы можете использовать collections.OrderedDict
из стандартной библиотеки Python только ключи (и значения как None
) для той же цели.
Обновление : По состоянию на Python 3.7 (и CPython 3.6), стандарт dict
будет гарантированно сохранить порядок и более производительные , чем OrderedDict
. (Однако для обратной совместимости и особенно читабельности вы можете продолжить использование OrderedDict
.)
Вот пример того, как использовать dict
в качестве упорядоченного набора, чтобы отфильтровать повторяющиеся элементы при сохранении порядка, тем самым эмулируя упорядоченный набор. Используйте dict
метод класса, fromkeys()
чтобы создать dict, затем просто попросите keys()
обратную.
>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']
>>> list(dict.fromkeys(keywords))
['foo', 'bar', 'baz']
dict.fromkeys()
. Но в этом случае порядок ключей сохраняется только в реализациях CPython 3.6+, поэтому OrderedDict
это более переносимое решение, когда порядок имеет значение.
keys = (1,2,3,1,2,1)
list(OrderedDict.fromkeys(keys).keys())
-> [1, 2, 3]
, python-3.7. Оно работает.
dict
, set
в Python 3.7+ , к сожалению , не сохраняет порядок.
Я могу сделать вас лучше, чем OrderedSet: boltons имеет чистый Python, 2/3-совместимый IndexedSet
тип , который не только упорядоченное множество, но также поддерживает индексирование (как со списками).
Просто pip install boltons
(или скопируйте setutils.py
в свою кодовую базу), импортируйте IndexedSet
и:
>>> from boltons.setutils import IndexedSet
>>> x = IndexedSet(list(range(4)) + list(range(8)))
>>> x
IndexedSet([0, 1, 2, 3, 4, 5, 6, 7])
>>> x - set(range(2))
IndexedSet([2, 3, 4, 5, 6, 7])
>>> x[-1]
7
>>> fcr = IndexedSet('freecreditreport.com')
>>> ''.join(fcr[:fcr.index('.')])
'frecditpo'
Все уникально и сохранено в порядке. Полное раскрытие: я написал IndexedSet
, но это также означает, что вы можете меня беспокоить, если есть какие-либо проблемы . :)
В то время как другие отмечают, что в Python нет встроенной реализации набора сохранения порядка вставки (пока), я чувствую, что в этом вопросе отсутствует ответ, в котором указано, что можно найти в PyPI .
Есть пакеты:
Некоторые из этих реализаций основаны на рецепте, опубликованном Раймондом Хеттингером в ActiveState. который также упоминается в других ответах здесь.
my_set[5]
)remove(item)
Обе реализации имеют O (1) для add(item)
и __contains__(item)
( item in my_set
).
set.union
, не работают на нем, хотя он наследуется collections.abc.Set
.
Если вы используете упорядоченный набор для поддержания отсортированного порядка, рассмотрите возможность использования реализации отсортированного набора из PyPI. Модуль sortedcontainers предоставляет SortedSet именно для этой цели. Некоторые преимущества: чистый Python, реализация fast-as-C, 100% охват модульных тестов, часы стресс-тестирования.
Установка из PyPI легко с pip:
pip install sortedcontainers
Обратите внимание, что если вы не можете pip install
, просто извлеките файлы sortedlist.py и sortedset.py из репозитория с открытым исходным кодом .
После установки вы можете просто:
from sortedcontainers import SortedSet
help(SortedSet)
Модуль sortedcontainers также поддерживает сравнение производительности с несколькими альтернативными реализациями.
Для комментария, который спрашивал о типе данных пакета Python, есть альтернативный тип данных SortedList, который можно использовать для эффективной реализации пакета.
SortedSet
класс там требует, чтобы члены были сопоставимы и хэшируемы.
set
а frozenset
также требуют, чтобы элементы были хэшируемыми. Сопоставимое ограничение является дополнением для SortedSet
, но это также очевидное ограничение.
В случае, если вы уже используете панды в своем коде, его Index
объект ведет себя почти как упорядоченный набор, как показано в этой статье .
Примеры из статьи:
indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])
indA & indB # intersection
indA | indB # union
indA - indB # difference
indA ^ indB # symmetric difference
indA.difference(indB)
, знак минус выполняет стандартное вычитание
Немного опоздал к игре, но я написал класс, setlist
как часть collections-extended
которого полностью реализует Sequence
иSet
>>> from collections_extended import setlist
>>> sl = setlist('abracadabra')
>>> sl
setlist(('a', 'b', 'r', 'c', 'd'))
>>> sl[3]
'c'
>>> sl[-1]
'd'
>>> 'r' in sl # testing for inclusion is fast
True
>>> sl.index('d') # so is finding the index of an element
4
>>> sl.insert(1, 'd') # inserting an element already in raises a ValueError
ValueError
>>> sl.index('d')
4
GitHub: https://github.com/mlenzen/collections-extended
Документация: http://collections-extended.lenzm.net/en/latest/
Там нет OrderedSet
в официальной библиотеке. Я делаю исчерпывающую таблицу всех структур данных для вашей справки.
DataStructure = {
'Collections': {
'Map': [
('dict', 'OrderDict', 'defaultdict'),
('chainmap', 'types.MappingProxyType')
],
'Set': [('set', 'frozenset'), {'multiset': 'collection.Counter'}]
},
'Sequence': {
'Basic': ['list', 'tuple', 'iterator']
},
'Algorithm': {
'Priority': ['heapq', 'queue.PriorityQueue'],
'Queue': ['queue.Queue', 'multiprocessing.Queue'],
'Stack': ['collection.deque', 'queue.LifeQueue']
},
'text_sequence': ['str', 'byte', 'bytearray']
}
Пакет ParallelRegression предоставляет класс упорядоченного набора setList (), который является более полным методом, чем параметры, основанные на рецепте ActiveState. Он поддерживает все методы, доступные для списков, и большинство, если не все методы, доступные для множеств.
Как отмечают другие ответы, как и для python 3.7+, dict упорядочен по определению. Вместо того, чтобы создавать подклассы, OrderedDict
мы можем создавать подклассы abc.collections.MutableSet
или typing.MutableSet
использовать ключи dict для хранения наших значений.
class OrderedSet(typing.MutableSet[T]):
"""A set that preserves insertion order by internally using a dict."""
def __init__(self, iterable: t.Iterator[T]):
self._d = dict.fromkeys(iterable)
def add(self, x: T) -> None:
self._d[x] = None
def discard(self, x: T) -> None:
self._d.pop(x)
def __contains__(self, x: object) -> bool:
return self._d.__contains__(x)
def __len__(self) -> int:
return self._d.__len__()
def __iter__(self) -> t.Iterator[T]:
return self._d.__iter__()
Тогда просто:
x = OrderedSet([1, 2, -1, "bar"])
x.add(0)
assert list(x) == [1, 2, -1, "bar", 0]
Я поместил этот код в небольшую библиотеку , так что любой может pip install
это сделать.
Для многих целей достаточно просто отсортированного вызова. Например
>>> s = set([0, 1, 2, 99, 4, 40, 3, 20, 24, 100, 60])
>>> sorted(s)
[0, 1, 2, 3, 4, 20, 24, 40, 60, 99, 100]
Если вы собираетесь использовать это несколько раз, при вызове отсортированной функции возникнут дополнительные издержки, так что вы можете захотеть сохранить результирующий список, если вы закончили изменять набор. Если вам нужно сохранить уникальные элементы и отсортированные, я согласен с предложением использовать OrderedDict из коллекций с произвольным значением, таким как None.
Таким образом, у меня также был небольшой список, в котором у меня была возможность ввести неуникальные значения.
Я искал наличие какого-то уникального списка, но потом понял, что тестирование существования элемента перед его добавлением работает просто отлично.
if(not new_element in my_list):
my_list.append(new_element)
Я не знаю, есть ли предостережения к этому простому подходу, но он решает мою проблему.