web-dev-qa-db-fra.com

Pourquoi `a == b ou c ou d` est toujours évalué à True?

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.

96
Kevin

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.

  1. Utilisez plusieurs == opérateurs à vérifier explicitement par rapport à chaque valeur:
    if name == "Kevin" or name == "Jon" or name == "Inbar":

  2. 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
132
Kevin

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.

0
user1854182