Pythonには順序セットがありますか?


477

Pythonには順序付き辞書があります。注文セットはどうですか?


18
逆に、バッグのことはどうですか?(順不同、非固有)
wim

19
@wim collections.CounterはPythonのバッグです。
flornquake 2013

1
何かが2回追加された場合はどうなりますか?ポジションはどうあるべきですか?
マッケイ

2
@McKay-collections.OrderDictの動作を追跡する場合でも、最初の追加の位置にあります
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
インタフェースは、通常の設定オブジェクトとまったく同じではありませんが、多くの必須メソッドは、次のような欠けていますupdateunionintersection
xApple

5
FYI、私は気づい少し変更したバージョンこの回答で引用レシピがされたは、PyPIに追加し、「注文・セット」として
ジェフリー・興

7
union同じクラスで2つのメソッドを呼び出すことは許可されていません。最後のものは「勝ち」、最初のものは実行時に存在しなくなります。これは、OrderedSet.union(括弧なし)が単一のオブジェクトを参照する必要があるためです。
Kevin

3
同じレシピに基づいているがCythonで実装されている "orderedset"パッケージもあります-pypi.python.org/pypi/orderedset
mbdevpl 2016

149

順序付きセットは、機能的には順序付き辞書の特殊なケースです。

辞書のキーは一意です。したがって、順序付けされたディクショナリの値を無視すると(たとえば、値を割り当てることにより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__

1
@Casebash:はい、1クラスを定義することもできますOrderedSetどのサブクラスをOrderedDictしてabc.Set、その後、定義__len____iter__および__contains__
Stephan202 2009年

1
@ Stephan202:残念ながら、ABCのコレクションはcollectionsにありますが、それ以外は良い提案です
u0b34a0f6ae 2009年

4
これは本当ですが、結果として多くの無駄なスペースがあり、最適ではないパフォーマンスにつながります。
Daniel Kats

3
追加; collections.OrderedDictは、Python 2.7でも使用できます。
Nurbldoff 2013

2
実行OrderedSet([1,2,3])するとTypeErrorが発生します。コンストラクタはどのように機能しますか?使用例がありません。
xApple

90

答えは「いいえ」ですが、同じ目的でcollections.OrderedDictキー(およびの値None)のみを使用してPython標準ライブラリから使用できます。

更新: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 Hossain

4
@AnwarHossain keys = (1,2,3,1,2,1) list(OrderedDict.fromkeys(keys).keys())-> [1, 2, 3]、python-3.7。できます。
raratiru

1
Python 3.7以降のSetでも順序が保持されると推測できますか?
user474491

2
@ user474491とは異なりdictsetPython 3.7以降では残念ながら順序が保持されません。
CZ

39

OrderedSetよりも1つ上手くできます。boltonsは、順序付けされたセットであるだけでなく、(リストと同様に)インデックス作成もサポートする、純粋なPythonのIndexedSet 2/3 互換タイプを持っています。

単純に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
新しい候補はcollections_extended.setlistです。のような関数set.unionは継承しcollections.abc.Setますが、機能しません。
timdiels

3
OrderedSet現在サポートしていますremove
warvariuc 2016年

17

並べ替え順序を維持するために順序付きセットを使用している場合は、PyPIからの並べ替えセットの実装の使用を検討してください。sortedcontainersのモジュールが提供したSortedSetをこの目的のためだけに。いくつかの利点:純粋なPython、Cとしての高速実装、100%の単体テストカバレッジ、何時間ものストレステスト。

PyPIからのインストールは、pipを使用すると簡単です。

pip install sortedcontainers

できない場合はpip install、単に、オープンソースリポジトリからsortedlist.pyファイルとsortedset.pyファイルをプルダウンしてください。

インストールしたら、簡単に次のことができます。

from sortedcontainers import SortedSet
help(SortedSet)

sortedcontainersモジュールは、いくつかの代替実装とのパフォーマンス比較も維持します

Pythonのbagデータ型について尋ねたコメントの代わりに、効率的にバッグを実装するために使用できるSortedListデータ型があります。


SortedSetそこのクラスでは、メンバーが比較可能でハッシュ可能である必要があることに注意してください。
gsnedders 2014年

4
@gsneddersビルトインsetfrozensetあり、要素がハッシュ可能であることも必要です。同等の制約はの追加ですSortedSetが、明らかな制約でもあります。
gotgenes

2
名前が示すように、これは秩序を維持しません。これは、sorted(set([sequence]))に他なりません。
ldmtwo

@ldmtwoどちらを参照しているかはわかりませんが、明確にするために、並べ替え済みコンテナの一部としてのSortedSetは並べ替え順序を維持します。
GrantJ 2018年

2
@GrantJ- 挿入順序を維持するかソート順序を維持するかの違いです。他のほとんどの答えは挿入順序に関するものです。私はあなたがあなたの最初の文に基づいてこれをすでに知っていると思いますが、それはおそらくldmtwoが言っていることです。
ジャスティン

9

コードですでにパンダを使用している場合、その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

この回答に例を含めることができますか?リンクはしばらくすると壊れる傾向があります。
Alechan

1
セット間の違いについては、実際にを使用する必要がありますindA.difference(indB)。マイナス記号は標準の減算を実行します
gg349

7

少し遅れてゲームに、私はクラスを書いているsetlistの一環として、collections-extendedその完全実装の両方SequenceSet

>>> 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のパッケージが提供してセットリストを()よりメソッド完全ActiveStateのレシピに基づいたオプションよりもセットクラスを命じました。リストで使用できるすべてのメソッド、およびセットで使用できるすべてではないにしてもほとんどのメソッドをサポートします。


2

他の回答が述べているように、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それを実行できます。


-4

多くの目的で、単にsortedを呼び出すだけで十分です。例えば

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

これを繰り返し使用する場合は、sorted関数を呼び出すことによってオーバーヘッドが発生するため、セットの変更が完了している限り、結果のリストを保存する必要があります。一意の要素を維持して並べ替える必要がある場合は、Noneなどの任意の値を持つコレクションからOrderedDictを使用するという提案に同意します。


43
OrderedSetの目的は、アイテムをセットに追加した順序でアイテムを取得できるようにすることです。例としては、SortedSetと呼ばれるかもしれません...
定期メンテナンス

-4

だから私はまた、一意でない値を導入する可能性があることを明らかにした小さなリストも持っていました。

私はある種のユニークなリストの存在を検索しましたが、追加する前に要素の存在をテストしても問題なく機能することに気付きました。

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

この単純なアプローチに警告があるかどうかはわかりませんが、それで問題が解決します。


このアプローチの主な問題は、実行をO(n)に追加することです。大きなリストでは遅くなることを意味します。Pythonの組み込みセットは、要素の追加を高速化するのに非常に適しています。しかし、単純なユースケースでは、確かに機能します!
Draconis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.