Python hat ein geordnetes Wörterbuch . Was ist mit einem bestellten Set?
collections.Counter
ist Pythons Tasche.
Python hat ein geordnetes Wörterbuch . Was ist mit einem bestellten Set?
collections.Counter
ist Pythons Tasche.
Antworten:
Hierfür gibt es ein geordnetes Set- Rezept (möglicher neuer Link ), auf das in der Python 2-Dokumentation verwiesen wird . Dies läuft auf Py2.6 oder höher und 3.0 oder höher ohne Änderungen. Die Schnittstelle ist fast genau die gleiche wie bei einem normalen Satz, außer dass die Initialisierung mit einer Liste erfolgen sollte.
OrderedSet([1, 2, 3])
Dies ist ein MutableSet, daher stimmt die Signatur für .union
nicht mit der von set überein. Da sie jedoch __or__
etwas Ähnliches enthält , kann sie leicht hinzugefügt werden:
@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
in derselben Klasse aufrufen dürfen . Der letzte wird "gewinnen" und der erste wird zur Laufzeit nicht existieren. Dies liegt daran, dass OrderedSet.union
(keine Eltern) sich auf ein einzelnes Objekt beziehen müssen .
Die Schlüssel eines Wörterbuchs sind eindeutig. Wenn man also die Werte in einem geordneten Wörterbuch ignoriert (z. B. indem man sie zuweist None
), hat man im Wesentlichen eine geordnete Menge.
Ab Python 3.1 gibt es collections.OrderedDict
. Das Folgende ist eine Beispielimplementierung eines OrderedSet. (Beachten Sie, dass nur wenige Methoden definiert oder überschrieben werden müssen: collections.OrderedDict
und collections.MutableSet
das schwere Heben.)
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
welche Unterklassen OrderedDict
und abc.Set
dann definieren __len__
, __iter__
und __contains__
.
collections
, aber ansonsten ein guter Vorschlag
OrderedSet([1,2,3])
wirft eine Typeerror. Wie funktioniert der Konstruktor überhaupt? Fehlendes Anwendungsbeispiel.
Die Antwort lautet Nein, aber Sie können collections.OrderedDict
aus der Python-Standardbibliothek nur Schlüssel (und Werte als None
) für denselben Zweck verwenden.
Update : Ab Python 3.7 (und CPython 3.6), Standard dict
ist garantiert , um zu erhalten , und ist leistungsfähiger als OrderedDict
. (Aus Gründen der Abwärtskompatibilität und insbesondere der Lesbarkeit möchten Sie diese jedoch möglicherweise weiterhin verwenden OrderedDict
.)
Hier ist ein Beispiel für die Verwendung dict
als geordneter Satz, um doppelte Elemente herauszufiltern und gleichzeitig die Reihenfolge beizubehalten, wodurch ein geordneter Satz emuliert wird. Verwenden Sie die dict
Klassenmethode fromkeys()
, um ein Diktat zu erstellen, und fragen Sie dann einfach nach der keys()
Rückseite.
>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']
>>> list(dict.fromkeys(keywords))
['foo', 'bar', 'baz']
dict.fromkeys()
. In diesem Fall bleibt die Schlüsselreihenfolge jedoch nur in CPython 3.6+ -Implementierungen erhalten. Dies OrderedDict
ist eine portablere Lösung, wenn es um die Reihenfolge geht.
keys = (1,2,3,1,2,1)
list(OrderedDict.fromkeys(keys).keys())
-> [1, 2, 3]
, Python-3.7. Es klappt.
dict
, set
in Python 3.7+ leider nicht beibehalten Ordnung.
Ich kann Ihnen eines besser machen als ein OrderedSet: Boltons hat einen reinen Python, 2/3-kompatiblen IndexedSet
Typ , der nicht nur ein geordneter Satz ist, sondern auch die Indizierung unterstützt (wie bei Listen).
Importieren Sie einfach pip install boltons
(oder kopieren Sie es setutils.py
in Ihre Codebasis) das IndexedSet
und:
>>> 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'
Alles ist einzigartig und in Ordnung gehalten. Vollständige Offenlegung: Ich habe das geschrieben IndexedSet
, aber das bedeutet auch, dass Sie mich nerven können, wenn es irgendwelche Probleme gibt . :) :)
Während andere darauf hingewiesen haben, dass es in Python (noch) keine integrierte Implementierung eines Satzes zur Beibehaltung der Einfügereihenfolge gibt, fehlt dieser Frage meines Erachtens eine Antwort, die angibt, was auf PyPI zu finden ist .
Es gibt die Pakete:
Einige dieser Implementierungen basieren auf dem Rezept von Raymond Hettinger an ActiveState, das auch in anderen Antworten hier erwähnt wird.
my_set[5]
)remove(item)
Beide Implementierungen haben O (1) für add(item)
und __contains__(item)
( item in my_set
).
set.union
jedoch nicht, obwohl es erbt collections.abc.Set
.
Wenn Sie den geordneten Satz verwenden, um eine sortierte Reihenfolge beizubehalten, sollten Sie eine Implementierung eines sortierten Satzes von PyPI in Betracht ziehen. Das sortedcontainers- Modul bietet zu diesem Zweck ein SortedSet . Einige Vorteile: Pure-Python, C-schnelle Implementierungen, 100% Unit-Test-Abdeckung, stundenlange Stresstests.
Die Installation von PyPI ist mit pip einfach:
pip install sortedcontainers
Beachten Sie, dass Sie, wenn Sie dies nicht können pip install
, einfach die Dateien sortedlist.py und sortedset.py aus dem Open-Source-Repository abrufen .
Einmal installiert, können Sie einfach:
from sortedcontainers import SortedSet
help(SortedSet)
Das Sortedcontainer-Modul führt auch einen Leistungsvergleich mit mehreren alternativen Implementierungen durch.
Für den Kommentar zum Python-Bag-Datentyp gibt es alternativ einen SortedList- Datentyp, mit dem ein Bag effizient implementiert werden kann.
SortedSet
Klasse dort Mitglieder vergleichbar und hashbar sein müssen.
set
und frozenset
auch Elemente müssen hashbar sein. Die vergleichbare Einschränkung ist die Ergänzung für SortedSet
, aber es ist auch eine offensichtliche Einschränkung.
Falls Sie in Ihrem Code bereits Pandas verwenden, Index
verhält sich das Objekt wie ein geordneter Satz, wie in diesem Artikel gezeigt .
Beispiele aus dem Artikel:
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)
führt das Minuszeichen eine Standard-Subtraktion durch
Ein bisschen spät zum Spiel, aber ich habe eine Klasse geschrieben, setlist
als Teil davon collections-extended
implementiert sowohl Sequence
undSet
>>> 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
Dokumentation: http://collections-extended.lenzm.net/en/latest/
Es gibt keine OrderedSet
in der offiziellen Bibliothek. Ich erstelle ein ausführliches Cheatsheet der gesamten Datenstruktur als Referenz.
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']
}
Das ParallelRegression- Paket bietet eine setList () - geordnete Set-Klasse, die methodisch vollständiger ist als die Optionen, die auf dem ActiveState-Rezept basieren. Es unterstützt alle für Listen verfügbaren Methoden und die meisten, wenn nicht alle für Sets verfügbaren Methoden.
Wie in anderen Antworten erwähnt, ist das Diktat wie in Python 3.7+ per Definition geordnet. Anstelle von Unterklassen können OrderedDict
wir Unterklassen verwenden abc.collections.MutableSet
oder typing.MutableSet
die Schlüssel des Diktats verwenden, um unsere Werte zu speichern.
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__()
Dann einfach:
x = OrderedSet([1, 2, -1, "bar"])
x.add(0)
assert list(x) == [1, 2, -1, "bar", 0]
Ich habe diesen Code in eine kleine Bibliothek gestellt , damit jeder es einfach pip install
kann.
Für viele Zwecke reicht es aus, einfach sortiert aufzurufen. Zum Beispiel
>>> 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]
Wenn Sie dies wiederholt verwenden, entsteht durch das Aufrufen der sortierten Funktion ein Overhead, sodass Sie die resultierende Liste möglicherweise speichern möchten, solange Sie mit dem Ändern des Satzes fertig sind. Wenn Sie eindeutige und sortierte Elemente beibehalten müssen, stimme ich dem Vorschlag zu, OrderedDict aus Sammlungen mit einem beliebigen Wert wie "Keine" zu verwenden.
Also hatte ich auch eine kleine Liste, in der ich eindeutig die Möglichkeit hatte, nicht eindeutige Werte einzuführen.
Ich suchte nach einer eindeutigen Liste, stellte dann aber fest, dass das Testen der Existenz des Elements vor dem Hinzufügen einwandfrei funktioniert.
if(not new_element in my_list):
my_list.append(new_element)
Ich weiß nicht, ob dieser einfache Ansatz Vorbehalte enthält, aber er löst mein Problem.