Cela fait ce que vous voulez et fonctionnera dans presque tous les cas:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
L'expression 'a','b' in ['b', 'a', 'foo', 'bar']
ne fonctionne pas comme prévu car Python l'interprète comme un tuple:
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Autres options
Il existe d'autres moyens d'exécuter ce test, mais ils ne fonctionneront pas pour autant de types d'entrées différents. Comme le souligne Kabie , vous pouvez résoudre ce problème en utilisant des ensembles ...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...quelquefois:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Les ensembles ne peuvent être créés qu'avec des éléments hachables. Mais l'expression du générateur all(x in container for x in items)
peut gérer presque tous les types de conteneurs. La seule exigence est qu'il container
soit réitérable (c'est-à-dire pas un générateur). items
peut être tout itérable du tout.
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
Tests de vitesse
Dans de nombreux cas, le test du sous-ensemble sera plus rapide que all
, mais la différence n'est pas choquante - sauf lorsque la question n'est pas pertinente car les ensembles ne sont pas une option. La conversion de listes en ensembles uniquement dans le but d'un test comme celui-ci n'en vaudra pas toujours la peine. Et la conversion de générateurs en ensembles peut parfois être un gaspillage incroyable, ralentissant les programmes de plusieurs ordres de grandeur.
Voici quelques repères à titre d'illustration. La plus grande différence vient quand les deux container
et items
sont relativement faibles. Dans ce cas, l'approche du sous-ensemble est d'environ un ordre de grandeur plus rapide:
>>> 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)
Cela ressemble à une grande différence. Mais tant qu'il container
s'agit d'un ensemble, il all
est toujours parfaitement utilisable à des échelles beaucoup plus grandes:
>>> 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)
L'utilisation des tests de sous-ensembles est encore plus rapide, mais seulement d'environ 5x à cette échelle. L'augmentation de la vitesse est due à l' c
implémentation rapide de Python set
, mais l'algorithme fondamental est le même dans les deux cas.
Si vous items
êtes déjà stocké dans une liste pour d'autres raisons, vous devrez les convertir en un ensemble avant d'utiliser l'approche de test de sous-ensemble. Ensuite, l'accélération tombe à environ 2,5x:
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Et si votre container
est une séquence, et doit d'abord être convertie, alors l'accélération est encore plus petite:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Le seul moment où nous obtenons des résultats désastreusement lents, c'est lorsque nous partons container
en séquence:
>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Et bien sûr, nous ne le ferons que si nous le devons. Si tous les éléments de bigseq
sont hachables, nous le ferons à la place:
>>> %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)
C'est juste 1,66 fois plus rapide que l'alternative ( set(bigseq) >= set(bigsubseq)
, chronométré ci-dessus à 4,36).
Les tests de sous-ensembles sont donc généralement plus rapides, mais pas avec une marge incroyable. D'un autre côté, regardons quand all
est plus rapide. Que faire si items
est long de dix millions de valeurs et est susceptible d'avoir des valeurs qui ne sont pas incluses 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)
La conversion du générateur en un ensemble s'avère être un gaspillage incroyable dans ce cas. Le set
constructeur doit consommer tout le générateur. Mais le comportement de court-circuit de all
garantit que seule une petite partie du générateur doit être consommée, c'est donc plus rapide qu'un test de sous-ensemble de quatre ordres de grandeur .
C'est un exemple extrême, certes. Mais comme cela le montre, vous ne pouvez pas supposer qu'une approche ou une autre sera plus rapide dans tous les cas.
The Upshot
La plupart du temps, la conversion container
en un ensemble en vaut la peine, du moins si tous ses éléments sont hachables. C'est parce que in
pour les ensembles est O (1), tandis que in
pour les séquences est O (n).
D'un autre côté, l'utilisation de tests de sous-ensembles n'en vaut probablement la peine que parfois. Faites-le certainement si vos éléments de test sont déjà stockés dans un ensemble. Sinon, ce all
n'est qu'un peu plus lent et ne nécessite aucun stockage supplémentaire. Il peut également être utilisé avec de gros générateurs d'objets, et fournit parfois une accélération massive dans ce cas.
set(['a', 'b']) <= set(['b','a','foo','bar'])
est une autre façon d'épeler la même chose, et semble "mathier".{'a', 'b'} <= {'b','a','foo','bar'}
Je suis à peu près sûr
in
qu'elle a une priorité plus élevée que,
si votre déclaration est interprétée comme'a', ('b' in ['b' ...])
, qui s'évalue ensuite'a', True
comme'b'
étant dans le tableau.Voir la réponse précédente pour savoir comment faire ce que vous voulez.
la source
Si vous souhaitez vérifier toutes vos correspondances d'entrée ,
si vous souhaitez vérifier au moins une correspondance ,
la source
L'analyseur Python a évalué cette instruction comme un tuple, où la première valeur était
'a'
, et la deuxième valeur est l'expression'b' in ['b', 'a', 'foo', 'bar']
(qui est évaluée àTrue
).Vous pouvez cependant écrire une fonction simple pour faire ce que vous voulez:
Et appelez-le comme:
la source
La raison pour laquelle je pense que c'est mieux que la réponse choisie est que vous n'avez vraiment pas besoin d'appeler la fonction 'all ()'. La liste vide a la valeur False dans les instructions IF, la liste non vide a la valeur True.
Exemple:
la source
Je dirais que nous pouvons même laisser ces crochets de côté.
la source
Les deux réponses présentées ici ne traiteront pas les éléments répétés. Par exemple, si vous testez si [1,2,2] est une sous-liste de [1,2,3,4], les deux renverront True. C'est peut-être ce que vous voulez faire, mais je voulais juste clarifier. Si vous voulez retourner false pour [1,2,2] dans [1,2,3,4], vous devez trier les deux listes et vérifier chaque élément avec un index mobile sur chaque liste. Juste une boucle for un peu plus compliquée.
la source
comment pouvez-vous être pythonique sans lambdas! .. à ne pas prendre au sérieux .. mais cette méthode fonctionne aussi:
laissez de côté la partie finale si vous voulez tester si l' une des valeurs est dans le tableau:
la source
filter
trouve un générateur. Vous auriez besoin de l'envelopperlist
si vous vouliez réellement obtenir un résultat que vous pourriez tester avec==
ou dans un contexte booléen (pour voir s'il est vide). Utiliser une compréhension de liste ou une expression de générateur dansany
ouall
est préférable.Voici comment je l'ai fait:
la source