J'écris un système de sécurité qui refuse l'accès aux utilisateurs non autorisés.
import sys
print("Hello. Please enter your name:")
name = sys.stdin.readline().strip()
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
Il accorde l'accès aux utilisateurs autorisés comme prévu, mais il permet également aux utilisateurs non autorisés!
Hello. Please enter your name:
Bob
Access granted.
Pourquoi cela se produit-il? J'ai clairement déclaré n'accorder l'accès que lorsque name
est égal à Kevin, Jon ou Inbar. J'ai également essayé la logique opposée, if "Kevin" or "Jon" or "Inbar" == name
, Mais le résultat est le même.
Dans de nombreux cas, Python ressemble et se comporte comme un anglais naturel, mais c'est un cas où cette abstraction échoue. Les gens peuvent utiliser des indices de contexte pour déterminer que "Jon" et "Inbar" sont des objets joints à le verbe "est égal", mais l'interprète Python est plus littéral).
if name == "Kevin" or "Jon" or "Inbar":
est logiquement équivalent à:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Ce qui, pour l'utilisateur Bob, équivaut à:
if (False) or ("Jon") or ("Inbar"):
L'opérateur or
choisit le premier argument avec un --- valeur de vérité :
if ("Jon"):
Et puisque "Jon" a une valeur de vérité positive, le bloc if
s'exécute. C'est ce qui provoque l'impression "Accès accordé" quel que soit le nom donné.
Tout ce raisonnement s'applique également à l'expression if "Kevin" or "Jon" or "Inbar" == name
. la première valeur, "Kevin"
, est vrai, donc le bloc if
s'exécute.
Il existe deux façons courantes de construire correctement ce conditionnel.
Utilisez plusieurs ==
opérateurs à vérifier explicitement par rapport à chaque valeur:if name == "Kevin" or name == "Jon" or name == "Inbar":
Composez une séquence de valeurs valides et utilisez l'opérateur in
pour tester l'appartenance:if name in {"Kevin", "Jon", "Inbar"}:
En général, le second devrait être préféré car il est plus facile à lire et aussi plus rapide:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"', setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
Problème d'ingénierie simple, allons-y simplement un peu plus loin.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
Mais, hérité du langage C, Python évalue la valeur logique d'un entier non nul comme True.
In [11]: if 3:
...: print ("yey")
...:
yey
Maintenant, Python s'appuie sur cette logique et vous permet d'utiliser des littéraux logiques tels que ou sur des entiers, et ainsi
In [9]: False or 3
Out[9]: 3
Finalement
In [4]: a==b or c or d
Out[4]: 3
La bonne façon de l'écrire serait:
In [13]: if a in (b,c,d):
...: print('Access granted')
Pour des raisons de sécurité, je vous suggère également de ne pas coder en dur les mots de passe.