Чи має Python замовлений набір?


477

У Python є впорядкований словник . А як із замовленим набором?


18
як щодо зворотнього, мішок речей? (не упорядкований та не унікальний)
wim

19
@wim collections.Counter- сумка Пітона.
flornquake

1
Що робити, якщо щось додається двічі? Якою має бути посада?
Маккей

2
@McKay - якби слідкувати за поведінкою колекцій. OrdDict все-таки буде в положенні початкового доповнення
wojtow

Відповіді:


206

Існує впорядкований набір (можливе нове посилання ) для цього, про який йдеться в Документації 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

6
Я вибрав свою власну відповідь, тому що посилання з документації робить це близьким до офіційної відповіді
Casebash

49
Інтерфейс не точно так же , як звичайний об'єкт набору, багато основні методи відсутні , такі як update, union, intersection.
xApple


7
Я впевнений, що вам не дозволено використовувати два методи unionв одному класі. Останній "виграє", а перший не буде існувати під час виконання. Це тому, що OrderedSet.union(немає паронів) має посилатися на один об'єкт.
Кевін

3
Існує також пакет «замовлений набір», який базується на тому самому рецепті, але реалізований у Cython - pypi.python.org/pypi/orderedset .
mbdevpl

149

Впорядкований набір функціонально є особливим випадком упорядкованого словника.

Клавіші словника унікальні. Таким чином, якщо нехтувати значеннями в упорядкованому словнику (наприклад, призначаючи їх 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__

1
@Casebash: так, один може знадобитися визначити клас , OrderedSetякі підкласи OrderedDictі abc.Setпотім визначити __len__, __iter__і __contains__.
Stephan202

1
@ Stephan202: На жаль, колекція Азбуки живе collections, але в іншому випадку хороша пропозиція
u0b34a0f6ae

4
Це правда, але у вас дуже багато витраченого місця, що призводить до неоптимальної продуктивності.
Даніель Кац

3
Доповнення; collection.OrderedDict також доступний у python 2.7.
Nurbldoff

2
Doing OrderedSet([1,2,3])піднімає TypeError. Як конструктор навіть працює? Відсутній приклад використання.
xApple

90

Відповідь - ні, але ви можете використовувати 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']

4
Можливо, варто згадати, що це також працює (швидше) з ваніллю dict.fromkeys(). Але в цьому випадку ключовий порядок зберігається лише в реалізаціях CPython 3.6+, тому OrderedDictце більш портативне рішення, коли питання має значення.
jez

1
не буде працювати, якщо значення не є рядковими
Anwar

4
@AnwarHossain keys = (1,2,3,1,2,1) list(OrderedDict.fromkeys(keys).keys())-> [1, 2, 3], python-3.7. Це працює.
raratiru

1
Чи можемо ми зробити висновок, що Set у Python 3.7+ зберегти порядок також?
користувач474491

2
@ user474491 На відміну від цього dict, setв Python 3.7+, на жаль, не зберігається порядок.
cz

39

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


39

Впровадження в PyPI

У той час як інші зазначають, що в Python (поки що) немає вбудованої реалізації збереження порядку вставки, але я відчуваю, що на це запитання відсутня відповідь, у якій зазначено, що там можна знайти в PyPI .

Є пакети:

Деякі з цих реалізацій засновані на рецепті, опублікованому Реймоном Хеттінгером для ActiveState, який також згадується в інших відповідях тут.

Деякі відмінності

  • впорядковано-набір (версія 1.1)
    • перевага: O (1) для пошуку за індексом (наприклад my_set[5])
  • oset (версія 0.1.3)
    • перевага: O (1) для remove(item)
    • недолік: мабуть O (n) для пошуку за індексом

Обидві реалізації мають O (1) для add(item)та __contains__(item)( item in my_set).


2
Новим претендентом є collection_extended.setlist . Функції на кшталт set.unionцього не працюють, хоча він успадковується collections.abc.Set.
timdiels

3
OrderedSetтепер підтримуєremove
warvariuc

17

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

4
@gsnedders вбудованих команд setі frozensetтакож вимагають елементи , щоб бути hashable. Порівняльне обмеження є доповненням SortedSet, але воно також явне обмеження.
gotgenes

2
Як випливає з назви, це не підтримує порядок. Це не краще, ніж сортування (набір ([послідовність]))?
ldmtwo

@ldmtwo Я не впевнений, на що ви звертаєтесь, але для того, щоб бути зрозумілим, SortedSet як частина сортованих контейнерів підтримує відсортований порядок.
GrantJ

2
@GrantJ - це різниця між тим, підтримує він порядок вставки або порядок сортування . Більшість інших відповідей стосуються порядку вставки. Я думаю, ви вже знаєте це на основі свого першого речення, але це, мабуть, те, що говорить ldmtwo.
Джастін

8

Якщо ви вже використовуєте панди у своєму коді, його 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

Чи можете ви включити приклад у цю відповідь? Через деякий час посилання розриваються.
Алечан

1
для різниці між множинами, що вам потрібно використовувати indA.difference(indB), знак мінус виконує стандартне віднімання
gg349,

7

Трохи пізно до гри, але я написав клас, 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/

PyPI: https://pypi.python.org/pypi/collections-extended


7

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

3

Пакет ParallelRegression забезпечує клас упорядкованого набору setList (), який є більш повним методом, ніж параметри, засновані на рецепті ActiveState. Він підтримує всі методи, доступні для списків, і більшість, якщо не всі методи, доступні для наборів.


2

В інших відповідях згадується, що стосовно 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його.


-4

Для багатьох цілей достатньо просто подзвонити на сортування. Наприклад

>>> 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.


43
Метою OrdersSet є можливість отримати елементи в тому порядку, який вони додали до набору. Ви, наприклад, могли назвати SortedSet ...
Періодичне обслуговування

-4

Тож у мене також був невеликий список, де я явно мав можливість ввести не унікальні значення.

Я шукав існування унікального списку якогось роду, але потім зрозумів, що тестування існування елемента перед його додаванням працює просто чудово.

if(not new_element in my_list):
    my_list.append(new_element)

Я не знаю, чи є застереження щодо цього простого підходу, але це вирішує мою проблему.


Основна проблема такого підходу полягає в тому, що додавання запусків в O (n). Це означає, що він стає повільніше, коли в великих списках. Вбудовані набори Python дуже добре роблять додавання елементів швидше. Але для простих випадків використання це, безумовно, працює!
Драконіс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.