update ::: Beitrag enthält einen Verweis auf falsche Behauptungen von schlechter Leistung von Sätzen im Vergleich zu frozensets. Ich behaupte, dass es immer noch sinnvoll ist, in dieser Instanz ein eingefrorenes Set zu verwenden, obwohl es nicht nötig ist, das Set selbst zu hacken, nur weil es semantisch korrekter ist. Obwohl ich in der Praxis nicht die zusätzlichen 6 Zeichen eingeben würde. Ich fühle mich nicht motiviert, den Post zu bearbeiten und zu bearbeiten, also sei nur darauf hingewiesen, dass die "Anschuldigungen" Links zu einigen falsch ausgeführten Tests verlinken. Die blutigen Details sind in den Kommentaren zusammengefasst. ::: Update
Der zweite Teil des Codes posted von Brandon Craig Rhodes ist recht gut, aber da er nicht auf meinen Vorschlag reagierte (nicht gut, als ich begann, diese zu schreiben, jedenfalls) eine frozenset über die Verwendung von Ich werde weitermachen und es selbst posten.
Die gesamte Grundlage des vorliegenden Projekts besteht darin, zu überprüfen, ob sich jeder einer Reihe von Werten (L1
) in einer anderen Gruppe von Werten befindet; Dieser Satz von Werten ist der Inhalt von L2
und L3
. Die Verwendung des Wortes "set" in diesem Satz ist bezeichnend: Obwohl L2
und L3
list
s sind, interessieren wir uns nicht wirklich für ihre listenähnlichen Eigenschaften, wie die Reihenfolge, in der ihre Werte sind oder wie viele von ihnen enthalten. Wir kümmern uns nur um die setzen (dort ist es wieder) der Werte, die sie zusammen enthalten.
Wenn diese Gruppe von Werten als Liste gespeichert wird, müssen Sie nacheinander die Listenelemente durchgehen und jedes einzelne überprüfen. Es ist relativ zeitaufwendig und es ist eine schlechte Semantik: Es ist wieder eine "Menge" von Werten, keine Liste. Also hat Python diese ordentlichen Settypen, die eine Reihe einzigartiger Werte enthalten, und kann Ihnen schnell sagen, ob ein Wert darin enthalten ist oder nicht. Dies funktioniert auf die gleiche Art und Weise, wie Pythons dict
Typen funktionieren, wenn Sie nach einem Schlüssel suchen.
Der Unterschied zwischen Sätzen und frozensets ist, dass Sets sind wandelbar, was bedeutet, dass sie nach der Erstellung geändert werden können. Die Dokumentation für beide Typen lautet here.
Da die Menge, die wir erstellen müssen, die Union der in L2
und L3
gespeicherten Werte nicht geändert wird, sobald sie erstellt wird, ist es semantisch angemessen, einen unveränderlichen Datentyp zu verwenden. Dies hat auch einige Leistungsvorteile. Nun, es macht Sinn, dass es einen Vorteil hätte; Sonst, warum hätte Python frozenset
als eingebaut?
Update ...
Brandon diese Frage beantwortet hat: der eigentliche Vorteil von gefrorenen Sets ist, dass ihre Unveränderlichkeit es ihnen ermöglicht, hashable zu sein, so dass sie Dictionary-Schlüssel oder Mitglieder anderer Sätze sein .
Ich habe einige informelle Timing-Tests durchgeführt, die die Geschwindigkeit für die Erstellung und Suche auf relativ großen (3000 Elemente) eingefrorenen und veränderbaren Mengen verglichen; Es gab keinen großen Unterschied. Dies widerspricht der obigen Verbindung, unterstützt aber das, was Brandon über ihre Identität sagt, aber für den Aspekt der Veränderbarkeit.
... Update
Jetzt, da frozensets unveränderlich sind, sie keine Update-Methode haben. Brandon verwendete die set.update
-Methode, um zu vermeiden, dass eine temporäre Liste auf dem Weg zur Erstellung erstellt und dann verworfen wird. Ich werde einen anderen Ansatz wählen.
items = (item for lst in (L2, L3) for item in lst)
Diese generator expression macht items
einen Iterator über, nacheinander, dessen Inhalt L2
und L3
. Nicht nur das, aber es macht es, ohne eine ganze Liste voller Zwischenobjekte zu erstellen. Die Verwendung geschachtelter for
Ausdrücke in Generatoren ist ein wenig verwirrend, aber ich schaffe es, sie zu sortieren, indem ich mich daran erinnere, dass sie in der gleichen Reihenfolge verschachteln, wie sie es tun würden, wenn Sie tatsächliche For-Schleifen schreiben würden, z.B.
def get_items(lists):
for lst in lists:
for item in lst:
yield item
Das generator function zum Generator Ausdruck äquivalent ist, die wir zu items
zugeordnet. Nun, außer dass es eine parametrisierte Funktionsdefinition anstelle einer direkten Zuweisung zu einer Variablen ist.
Wie auch immer, genug Abschweifung. Die große Sache mit Generatoren ist, dass sie eigentlich gar nichts machen. Nun, zumindest nicht sofort: Sie richten nur Arbeit ein, die später erledigt werden muss, wenn der Generatorausdruck iteriert ist. Dies wird formal als faul bezeichnet. Wir werden das tun (nun, ich bin es sowieso), indem ich items
an die frozenset
Funktion übergebe, die darüber iteriert und ein frostiges kaltes gefrorenes Set zurückgibt.
unwanted = frozenset(items)
Sie tatsächlich konnte die letzten beiden Zeilen, kombiniert durch den Generator Ausdruck direkt im Aufruf zu frozenset
setzen:
unwanted = frozenset(item for lst in (L2, L3) for item in lst)
Dieser ordentlichen syntaktischen Trick funktioniert, solange die durch den Generator Ausdruck erstellt iterator ist der einzige Parameter für die Funktion, die Sie anrufen. Andernfalls müssen Sie es in seine übliche separate Klammer schreiben, genau so, als ob Sie ein Tupel als Argument für die Funktion übergeben würden.
Jetzt können wir eine neue Liste auf die gleiche Weise wie Brandon, mit einem list comprehension erstellen. Diese verwenden die gleiche Syntax wie Generator Ausdrücke und tun im Grunde die gleiche Sache, mit der Ausnahme, dass sie begierig statt faul sind (wieder, das sind tatsächliche Fachbegriffe), so dass sie direkt zur Arbeit Iterieren über die Elemente und erstellen eine Liste von ihnen.
L4 = [item for item in L1 if item not in unwanted]
Dies ist gleichbedeutend mit einem Generator Ausdruck list
zum Übergeben, z.B.
L4 = list(item for item in L1 if item not in unwanted)
aber mehr idiomatische.
So wird dies erstellen die Liste L4
, die Elemente von L1
enthält, die nicht entweder in L2
waren oder L3
, die Aufrechterhaltung der Ordnung, dass sie ursprünglich in und die Zahl derer, die dort waren.
Wenn Sie nur wissen wollen die Werte in L1
sind aber nicht in L2
oder L3
, es ist viel einfacher: Sie schaffen nur diesen Satz:
L1_unique_values = set(L1) - unwanted
Sie eine Liste machen aus davon, as does st0le, aber das könnte nicht wirklich das sein, was Sie wollen. Wenn Sie wirklich wollen, tun die gesetzt von Werten, die nur in L1
zu finden sind, können Sie einen sehr guten Grund haben zu halten, dass gesetzt als set
oder in der Tat ein frozenset
:
L1_unique_values = frozenset(L1) - unwanted
... Annnnd, jetzt etwas ganz anderes:
from itertools import ifilterfalse, chain
L4 = list(ifilterfalse(frozenset(chain(L2, L3)).__contains__, L1))
Es gibt keinen einzigen richtigen Weg, dies zu tun, bis Sie entscheiden, ob Sie sich um Dubletten und Bestellungen kümmern oder sich nicht darum kümmern. Wahrscheinlich eine Art Listenverständnis oder Satzarbeit, je nachdem, was dir wichtig ist. – istruble
Ist es auch OK anzunehmen, dass alle Elemente in den Listen die ganze Zeit über hashbar sind? Wenn nicht oder manchmal nicht, wäre das sehr wichtig. – martineau
Warum verwenden Sie nicht zuerst Sätze? Dann würde deine "Arithmetik" funktionieren. – poke