Vérifiez si quelque chose est (pas) dans une liste en Python

314

J'ai une liste de tuples en Python , et j'ai une condition où je veux prendre la branche UNIQUEMENT si le tuple n'est pas dans la liste (s'il est dans la liste, alors je ne veux pas prendre la branche if)

if curr_x -1 > 0 and (curr_x-1 , curr_y) not in myList: 

    # Do Something

Mais cela ne fonctionne pas vraiment pour moi. Qu'est ce que j'ai mal fait?

Zack
la source
1
Notez que 3 -1 > 0 and (4-1 , 5) not in []Truedonc l'erreur n'est pas celle de la priorité de l'opérateur.
Dan D.
6
Que voulez-vous dire par "ne pas vraiment travailler pour moi"? À quoi vous attendez-vous? Que se passe-t-il réellement? Quel contenu de liste exact déclenche le problème?
Karl Knechtel
Pourquoi ne pas essayer myList.count((curr_x, curr_y)), si ce (curr_x, curr_y)n'est pas le cas myList, le résultat sera0
LittleLittleQ
2
Comment cette question «mon code ne fonctionne pas vraiment pour moi» a-t-elle pu obtenir 297 votes positifs? Veuillez nous donner un exemple reproductible minimal .
gerrit

Réponses:

503

Le bogue est probablement ailleurs dans votre code, car il devrait fonctionner correctement:

>>> 3 not in [2, 3, 4]
False
>>> 3 not in [4, 5, 6]
True

Ou avec des tuples:

>>> (2, 3) not in [(2, 3), (5, 6), (9, 1)]
False
>>> (2, 3) not in [(2, 7), (7, 3), "hi"]
True
orlp
la source
11
@Zack: si vous ne le saviez pas, vous pourriez le faireif not ELEMENT in COLLECTION:
ninjagecko
@ninjagecko: selon le type de conteneur qui pourrait être moins efficace, voire incorrect. Voir par exemple les filtres de bloom .
orlp
14
@nightcracker Cela n'a aucun sens car cela A not in Bse réduit à faire not B.__contains__(A)ce qui est le même que ce qui not A in Best réduit à ce qui est not B.__contains__(A).
Dan D.
1
Oh wow, j'aurais pu jurer que Python avait quelque chose comme ça __notcontains__. Je suis désolé, alors ce que j'ai dit n'est que des conneries.
orlp
2
@ std''OrgnlDave La seule façon qui pourrait arriver est d' notavoir une priorité plus élevée que celle inqui ne l'est pas. Considérez le résultat ast.dump(ast.parse("not A in B").body[0])dont les résultats dans "Expr(value=UnaryOp(op=Not(), operand=Compare(left=Name(id='A', ctx=Load()), ops=[In()], comparators=[Name(id='B', ctx=Load())])))"Si notgroupé étroitement à A, on aurait pu s'attendre à ce que le résultat soit "Expr(value=Compare(left=UnaryOp(op=Not(), operand=Name(id='A', ctx=Load())), ops=[In()], comparators=[Name(id='B', ctx=Load())]))"qui est l'analyse "(not A) in B".
Dan D.
20

Comment puis-je vérifier si quelque chose est (pas) dans une liste en Python?

La solution la moins chère et la plus lisible utilise l' inopérateur (ou dans votre cas spécifique not in). Comme mentionné dans la documentation,

Les opérateurs inet not intest d'adhésion. x in sévalue Truesi xest membre de s, et Falsesinon. x not in srenvoie la négation de x in s.

Aditionellement,

L'opérateur not inest défini pour avoir la valeur vraie inverse de in.

y not in xest logiquement le même que not y in x.

Voici quelques exemples:

'a' in [1, 2, 3]
# False

'c' in ['a', 'b', 'c']
# True

'a' not in [1, 2, 3]
# True

'c' not in ['a', 'b', 'c']
# False

Cela fonctionne également avec les tuples, car les tuples sont lavables (en conséquence du fait qu'ils sont également immuables):

(1, 2) in [(3, 4), (1, 2)]
#  True

Si l'objet sur le RHS définit une __contains__()méthode, l' inappellera en interne, comme indiqué dans le dernier paragraphe de la section Comparaisons de la documentation.

... inet not in, sont pris en charge par des types qui sont itérables ou implémentent la __contains__()méthode. Par exemple, vous pourriez (mais ne devriez pas) faire ceci:

[3, 2, 1].__contains__(1)
# True

incourt-circuits, donc si votre élément est au début de la liste, il est inévalué plus rapidement:

lst = list(range(10001))
%timeit 1 in lst
%timeit 10000 in lst  # Expected to take longer time.

68.9 ns ± 0.613 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
178 µs ± 5.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Si vous voulez faire plus que simplement vérifier si un élément est dans une liste, il existe des options:

  • list.indexpeut être utilisé pour récupérer l'index d'un élément. Si cet élément n'existe pas, un ValueErrorest levé.
  • list.count peut être utilisé si vous voulez compter les occurrences.

Le problème XY: Avez-vous pensé à sets?

Posez-vous ces questions:

  • avez-vous besoin de vérifier si un élément est dans la liste plus d'une fois?
  • Cette vérification est-elle effectuée à l'intérieur d'une boucle ou d'une fonction appelée à plusieurs reprises?
  • Les articles que vous stockez sur votre liste sont-ils lavables? IOW, pouvez-vous faire appel hashà eux?

Si vous avez répondu «oui» à ces questions, vous devriez plutôt utiliser un set. Un intest d'appartenance sur lists est une complexité temporelle O (n). Cela signifie que python doit effectuer une analyse linéaire de votre liste, visiter chaque élément et le comparer à l'élément de recherche. Si vous effectuez cette opération à plusieurs reprises, ou si les listes sont volumineuses, cette opération entraînera une surcharge.

setles objets, d'autre part, hachent leurs valeurs pour la vérification d'appartenance à temps constant. La vérification se fait également en utilisant in:

1 in {1, 2, 3} 
# True

'a' not in {'a', 'b', 'c'}
# False

(1, 2) in {('a', 'c'), (1, 2)}
# True

Si vous avez la malchance que l'élément que vous recherchez / ne recherchez pas se trouve à la fin de votre liste, python aura parcouru la liste jusqu'à la fin. Cela ressort clairement des horaires ci-dessous:

l = list(range(100001))
s = set(l)

%timeit 100000 in l
%timeit 100000 in s

2.58 ms ± 58.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
101 ns ± 9.53 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Pour rappel, c'est une option appropriée tant que les éléments que vous stockez et recherchez sont lavables. IOW, ils devraient être soit des types immuables, soit des objets à implémenter __hash__.

cs95
la source
2
Les ensembles ne sont pas toujours une option (par exemple, lorsqu'ils ont une liste d'éléments modifiables). Pour les grandes collections: la création de l'ensemble pour une recherche est de toute façon O (n) et peut doubler votre utilisation de la mémoire. Si vous n'avez pas encore de recherche, ce n'est pas toujours le meilleur choix pour en faire / maintenir une.
wim