У 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
. Далі наведено приклад реалізації OrdersSet. (Зауважте, що лише кілька методів потрібно визначити або відмінити: 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()
для створення диктату, а потім просто попросіть 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+, на жаль, не зберігається порядок.
Я можу зробити вас краще, ніж OrdersSet: 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, швидкі реалізації C, 100% тестовий покрив, години стрес-тестування.
Установка з PyPI легко за допомогою pip:
pip install sortedcontainers
Зауважте, що якщо ви не можете pip install
, просто витягніть файли sortedlist.py та sortedset.py із сховища з відкритим кодом .
Після встановлення ви можете просто:
from sortedcontainers import SortedSet
help(SortedSet)
Модуль сортованих контейнерів також підтримує порівняння продуктивності з кількома альтернативними реалізаціями.
Для коментаря, який запитував про тип даних пакета Python, існує альтернатива типу даних SortedList, який може бути використаний для ефективної реалізації пакета.
SortedSet
клас там вимагає, щоб члени були порівнянними та хешированными.
set
і frozenset
також вимагають елементи , щоб бути hashable. Порівняльне обмеження є доповненням 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+, диктант впорядкований за визначенням. Замість підкласифікації OrderedDict
ми можемо підклас abc.collections.MutableSet
або typing.MutableSet
використовувати клавіші диктанту для зберігання наших значень.
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]
Якщо ви збираєтеся використовувати це неодноразово, виникатимуть накладні витрати, викликаючи відсортовану функцію, так що ви, можливо, захочете зберегти отриманий список до тих пір, поки ви закінчите змінювати набір. Якщо вам потрібно підтримувати унікальні елементи та сортувати, я погоджуюся з пропозицією використовувати OrdersDict із колекцій з довільним значенням, таким як None.
Тож у мене також був невеликий список, де я явно мав можливість ввести не унікальні значення.
Я шукав існування унікального списку якогось роду, але потім зрозумів, що тестування існування елемента перед його додаванням працює просто чудово.
if(not new_element in my_list):
my_list.append(new_element)
Я не знаю, чи є застереження щодо цього простого підходу, але це вирішує мою проблему.