Dies macht, was Sie wollen, und funktioniert in fast allen Fällen:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
Der Ausdruck 'a','b' in ['b', 'a', 'foo', 'bar']
funktioniert nicht wie erwartet, da Python ihn als Tupel interpretiert:
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Andere Optionen
Es gibt andere Möglichkeiten, diesen Test auszuführen, aber sie funktionieren nicht für so viele verschiedene Arten von Eingaben. Wie Kabie betont , können Sie dieses Problem mit Sets lösen ...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...manchmal:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Sets können nur mit hashbaren Elementen erstellt werden. Der Generatorausdruck all(x in container for x in items)
kann jedoch fast jeden Containertyp verarbeiten. Die einzige Voraussetzung ist, dass container
sie erneut iterierbar ist (dh kein Generator). items
kann überhaupt iterierbar sein.
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
Geschwindigkeitstests
In vielen Fällen ist der Teilmengen-Test schneller als all
, aber der Unterschied ist nicht schockierend - außer wenn die Frage irrelevant ist, weil Mengen keine Option sind. Das Konvertieren von Listen in Sets nur zum Zweck eines solchen Tests ist nicht immer die Mühe wert. Und das Umrüsten von Generatoren in Sets kann manchmal unglaublich verschwenderisch sein und Programme um viele Größenordnungen verlangsamen.
Hier einige Benchmarks zur Veranschaulichung. Der größte Unterschied kommt , wenn beide container
und items
relativ klein sind. In diesem Fall ist der Teilmengenansatz um eine Größenordnung schneller:
>>> smallset = set(range(10))
>>> smallsubset = set(range(5))
>>> %timeit smallset >= smallsubset
110 ns ± 0.702 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit all(x in smallset for x in smallsubset)
951 ns ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Das sieht nach einem großen Unterschied aus. Aber solange container
es sich um ein Set handelt, all
ist es in weitaus größeren Maßstäben immer noch perfekt verwendbar:
>>> bigset = set(range(100000))
>>> bigsubset = set(range(50000))
>>> %timeit bigset >= bigsubset
1.14 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit all(x in bigset for x in bigsubset)
5.96 ms ± 37 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Das Verwenden von Teilmengen-Tests ist immer noch schneller, jedoch nur um das Fünffache in dieser Skala. Der Geschwindigkeitsschub ist auf die schnelle c
Implementierung von Python zurückzuführen set
, aber der grundlegende Algorithmus ist in beiden Fällen der gleiche.
Wenn Sie items
aus anderen Gründen bereits in einer Liste gespeichert sind, müssen Sie diese in eine Menge konvertieren, bevor Sie den Testansatz für Teilmengen verwenden können. Dann sinkt die Beschleunigung auf etwa das 2,5-fache:
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Und wenn Ihr container
eine Sequenz ist und zuerst konvertiert werden muss, ist die Beschleunigung noch geringer:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Das einzige Mal, wenn wir katastrophal langsame Ergebnisse erzielen, ist, wenn wir container
als Sequenz gehen:
>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Und das machen wir natürlich nur, wenn wir müssen. Wenn alle Elemente in bigseq
hashable sind, tun wir dies stattdessen:
>>> %timeit bigset = set(bigseq); all(x in bigset for x in bigsubseq)
7.24 ms ± 78 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Das ist nur 1,66x schneller als die Alternative ( set(bigseq) >= set(bigsubseq)
oben auf 4,36 eingestellt).
Das Testen von Teilmengen ist also im Allgemeinen schneller, aber nicht mit einem unglaublichen Vorsprung. Schauen wir uns andererseits an, wann all
es schneller geht. Was ist, wenn items
zehn Millionen Werte lang sind und wahrscheinlich Werte enthalten, die nicht vorhanden sind container
?
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); set(bigset) >= set(hugeiter)
13.1 s ± 167 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); all(x in bigset for x in hugeiter)
2.33 ms ± 65.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Das Umrüsten des Generators in ein Set erweist sich in diesem Fall als unglaublich verschwenderisch. Der set
Konstruktor muss den gesamten Generator verbrauchen. Das Kurzschlussverhalten von all
stellt jedoch sicher, dass nur ein kleiner Teil des Generators verbraucht werden muss, sodass es um vier Größenordnungen schneller als ein Teilmengen-Test ist .
Dies ist zugegebenermaßen ein extremes Beispiel. Aber wie es zeigt, können Sie nicht davon ausgehen, dass der eine oder andere Ansatz in allen Fällen schneller ist.
Das Ergebnis
Die Konvertierung container
in eine Menge lohnt sich meistens, zumindest wenn alle Elemente hashbar sind. Das liegt daran, dass in
für Mengen O (1) ist, während in
für Sequenzen O (n) ist.
Auf der anderen Seite lohnt es sich wahrscheinlich nur manchmal, Teilmengen-Tests durchzuführen. Tun Sie dies auf jeden Fall, wenn Ihre Testobjekte bereits in einem Set gespeichert sind. Ansonsten all
ist es nur wenig langsamer und benötigt keinen zusätzlichen Speicher. Es kann auch mit großen Generatoren von Gegenständen verwendet werden und bietet in diesem Fall manchmal eine massive Beschleunigung.
set(['a', 'b']) <= set(['b','a','foo','bar'])
ist eine andere Möglichkeit, dasselbe zu buchstabieren, und sieht "mathematischer" aus.{'a', 'b'} <= {'b','a','foo','bar'}
Ich bin mir ziemlich sicher
in
,,
dass Ihre Anweisung eine höhere Priorität hat, als dass Ihre Anweisung so interpretiert wird'a', ('b' in ['b' ...])
, dass sie ausgewertet wird,'a', True
da sie'b'
sich im Array befindet.In der vorherigen Antwort erfahren Sie, wie Sie das tun, was Sie wollen.
quelle
Wenn Sie alle Ihre Eingabeübereinstimmungen überprüfen möchten ,
Wenn Sie mindestens eine Übereinstimmung überprüfen möchten ,
quelle
Der Python-Parser hat diese Anweisung als Tupel ausgewertet, wobei der erste Wert war
'a'
, und der zweite Wert ist der Ausdruck'b' in ['b', 'a', 'foo', 'bar']
(der ausgewertet wirdTrue
).Sie können eine einfache Funktion schreiben und tun, was Sie wollen:
Und nenne es wie:
quelle
Der Grund, warum ich denke, dass dies besser ist als die gewählte Antwort, ist, dass Sie die Funktion 'all ()' wirklich nicht aufrufen müssen. Leere Liste wird in IF-Anweisungen mit False bewertet, nicht leere Liste mit True.
Beispiel:
quelle
Ich würde sagen, wir können diese eckigen Klammern sogar weglassen.
quelle
Beide hier vorgestellten Antworten behandeln keine wiederholten Elemente. Wenn Sie beispielsweise testen, ob [1,2,2] eine Unterliste von [1,2,3,4] ist, geben beide True zurück. Das ist vielleicht das, was Sie vorhaben, aber ich wollte es nur klarstellen. Wenn Sie in [1,2,3,4] für [1,2,2] false zurückgeben möchten, müssen Sie beide Listen sortieren und jedes Element mit einem sich bewegenden Index für jede Liste überprüfen. Nur eine etwas kompliziertere for-Schleife.
quelle
Wie kann man ohne Lambdas pythonisch sein? .. nicht ernst zu nehmen .. aber so funktioniert es auch:
Lassen Sie den Endteil weg, wenn Sie testen möchten, ob sich einer der Werte im Array befindet:
quelle
filter
sich ein Generator befindet, nicht wie vorgesehen funktioniert . Sie müssten es einpacken,list
wenn Sie tatsächlich ein Ergebnis erhalten möchten, das Sie mit==
oder in einem booleschen Kontext testen können (um zu sehen, ob es leer ist). Die Verwendung eines Listenverständnisses oder eines Generatorausdrucks inany
oderall
ist vorzuziehen.So habe ich es gemacht:
quelle