У 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)
Я не знаю, чи є застереження щодо цього простого підходу, але це вирішує мою проблему.