Kevin
Estoy escribiendo un sistema de seguridad que niega el acceso a usuarios no autorizados.
name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")
Otorga acceso a usuarios autorizados como se esperaba, ¡pero también deja entrar a usuarios no autorizados!
Hello. Please enter your name: Bob
Access granted.
¿Por qué ocurre esto? He declarado claramente que solo conceda acceso cuando name
es igual a Kevin, Jon o Inbar. También he probado la lógica opuesta, if "Kevin" or "Jon" or "Inbar" == name
Pero el resultado es el mismo.
Esta pregunta pretende ser el objetivo duplicado canónico de este problema tan común. Hay otra pregunta popular ¿Cómo probar múltiples variables para la igualdad contra un solo valor? que tiene el mismo problema fundamental, pero los objetivos de comparación están invertidos. Esta pregunta no debe cerrarse como un duplicado de esa, ya que este problema lo encuentran los recién llegados a Python que pueden tener dificultades para aplicar el conocimiento de la pregunta invertida a su problema.
En muchos casos, Python se ve y se comporta como un inglés natural, pero este es un caso en el que la abstracción falla. Las personas pueden usar pistas de contexto para determinar que “Jon” e “Inbar” son objetos unidos al verbo “equals”, pero el intérprete de Python tiene una mentalidad más literal.
if name == "Kevin" or "Jon" or "Inbar":
es lógicamente equivalente a:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Lo cual, para el usuario Bob, es equivalente a:
if (False) or ("Jon") or ("Inbar"):
los or
operador elige el primer argumento con un positivo valor de verdad:
if "Jon":
Y dado que “Jon” tiene un valor de verdad positivo, el if
se ejecuta el bloque. Eso es lo que hace que se imprima “Acceso concedido” independientemente del nombre dado.
Todo este razonamiento también se aplica a la expresión if "Kevin" or "Jon" or "Inbar" == name
. el primer valor, "Kevin"
es cierto, por lo que el if
se ejecuta el bloque.
Hay dos formas comunes de construir correctamente este condicional.
-
Usar múltiples
==
operadores para comprobar explícitamente cada valor:if name == "Kevin" or name == "Jon" or name == "Inbar":
-
Componga una colección de valores válidos (un conjunto, una lista o una tupla, por ejemplo), y use el
in
operador para probar la membresía:if name in {"Kevin", "Jon", "Inbar"}:
En general, de los dos, se debe preferir el segundo, ya que es más fácil de leer y también más rápido:
>>> 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
Para aquellos que quieran pruebas de que if a == b or c or d or e: ...
de hecho se analiza así. el incorporado ast
módulo proporciona una respuesta:
>>> import ast
>>> ast.parse("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
body=BoolOp(
op=Or(),
values=[
Compare(
left=Name(id='a', ctx=Load()),
ops=[
Eq()],
comparators=[
Name(id='b', ctx=Load())]),
Name(id='c', ctx=Load()),
Name(id='d', ctx=Load()),
Name(id='e', ctx=Load())]))
Como se puede ver, es el operador booleano or
aplicado a cuatro subexpresiones: comparación a == b
; y expresiones simples c
, d
y e
.
-
¿Hay alguna razón específica para elegir una tupla?
("Kevin", "Jon", "Inbar")
en lugar de un conjunto{"Kevin", "Jon", "Inbar"}
?– Humano
14 de junio de 2019 a las 12:04
-
En realidad, no, ya que ambos funcionan si todos los valores se pueden modificar. La prueba de membresía de conjuntos tiene una mayor complejidad de O grande que la prueba de membresía de tuplas, pero construir un conjunto es un poco más costoso que construir una tupla. Creo que es en gran parte un lavado para colecciones pequeñas como estas. Jugando con el tiempo,
a in {b, c, d}
es aproximadamente el doble de rápido quea in (b, c, d)
en mi maquina Algo en lo que pensar si se trata de una pieza de código crítica para el rendimiento.–Kevin
14 de junio de 2019 a las 12:13
-
¿Tupla o lista cuando se usa ‘in’ en una cláusula ‘if’? recomienda establecer literales para las pruebas de pertenencia. Actualizaré mi publicación.
–Kevin
14 de junio de 2019 a las 12:20
-
En Python moderno, reconoce que el conjunto es una constante y lo convierte en un
frozenset
en cambio, por lo que la sobrecarga del conjunto de construcción no está allí.dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
– endolito
16 mayo 2020 a las 17:48
7u5h4r
Hay 3 controles de condición en if name == "Kevin" or "Jon" or "Inbar":
- nombre == “Kevin”
- “Jon”
- “En el bar”
y esta sentencia if es equivalente a
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Ya que elif "Jon"
siempre será verdadero, por lo que se otorga acceso a cualquier usuario
Solución
Puede usar cualquiera de los siguientes métodos
Rápido
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Lento
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Código lento + innecesario
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Deepthi Tabitha Bennet
Resumiendo todas las respuestas existentes
(Y agregando algunos de mis puntos)
Explicación :
if name == "Kevin" or "Jon" or "Inbar":
es lógicamente equivalente a:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Lo cual, para el usuario Bob, es equivalente a:
if (False) or ("Jon") or ("Inbar"):
NOTA: Python evalúa el valor lógico de cualquier número entero distinto de cero como True
. Por lo tanto, todas las listas, conjuntos, cadenas, etc. que no están vacíos son evaluables y devuelven True
los or
El operador elige el primer argumento con un valor de verdad positivo.
Por lo tanto, “Jon” tiene un valor de verdad positivo y el bloque if se ejecuta, ya que ahora es equivalente a
if (False) or (True) or (True):
Eso es lo que hace que se imprima “Acceso concedido” independientemente del nombre introducido.
Soluciones :
Solución 1: Usar múltiples ==
operadores para verificar explícitamente cada valor
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solución 2: Componga una colección de valores válidos (un conjunto, una lista o una tupla, por ejemplo), y use el in
operador para probar la membresía (método preferido más rápido)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
O
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solución 3: Usa lo básico (y no muy eficiente) if-elif-else
estructura
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Problema de ingeniería simple, vamos a simplificarlo un poco más.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
Pero, heredado del lenguaje C, Python evalúa el valor lógico de un entero distinto de cero como Verdadero.
In [11]: if 3:
...: print ("yey")
...:
yey
Ahora, Python se basa en esa lógica y le permite usar literales lógicos como o en números enteros, y así
In [9]: False or 3
Out[9]: 3
Finalmente
In [4]: a==b or c or d
Out[4]: 3
La forma correcta de escribirlo sería:
In [13]: if a in (b,c,d):
...: print('Access granted')
Por seguridad, también te sugiero que no codifiques contraseñas.
niamulbengali
Las listas, conjuntos, cadenas, etc. no vacíos son evaluables y, por lo tanto, devuelven True.
Por eso, cuando dices:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
En realidad estás diciendo:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Dado que al menos uno de “John” e “Inbar” no es una cadena vacía, ¡la expresión completa siempre devuelve True!
La solución:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
o:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
-
bueno de lo contrario, pero “En realidad estás diciendo:” es equivocadoasí no es como
or
obras. El valor de la expresión es"John"
noTrue
.– Antti Haapala — Слава Україні
11/04/2021 a las 18:50
mkrieger1
Enfoques
Cómo un científico de datos aborda este problema
La forma más sencilla posible es eliminar la necesidad de operadores de comparación y utilizar una lista. Esto se ve impresionante en los sistemas de seguridad porque aprendes a acceder a los ORM.
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
O bien, puede parecerse a la exacto mismo código anterior, simplemente coloque la lista de usuarios registrados en su propia lista:
user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}
if user in users:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Si desea completar este protocolo de forma segura sin riesgo de ataque, configure parámetros dobles. Esto verificaría su mini-ORM para first
y last
campos de nombre, así como un password
o secret question
llave. Los objetos se pueden ordenar de esta manera si desea realizar una carga diferida eficiente de las credenciales de usuario sin hash:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
El bucle consumirá solamente los valores obtenidos para ahorrar tiempo y energía en su sistema:
A continuación, puede hacer algo con la lista iterada:
for j in lazy_range(10):
do_something_here(j)
Este problema se puede abordar desde cualquier ángulo: gestión de memoria, seguridad o simplemente mediante una lista orgánica o un ORM empaquetado.
-
bueno de lo contrario, pero “En realidad estás diciendo:” es equivocadoasí no es como
or
obras. El valor de la expresión es"John"
noTrue
.– Antti Haapala — Слава Україні
11/04/2021 a las 18:50
Las variaciones de este problema incluyen
x or y in z
,x and y in z
,x != y and z
y algunos otros Si bien no es exactamente idéntica a esta pregunta, la causa raíz es la misma para todos ellos. Solo quería señalar eso en caso de que alguien haya cerrado su pregunta como duplicado de esto y no estaba seguro de qué tan relevante es para ellos.– Aran Fey
11 de abril de 2019 a las 8:55
Consulte también, por ejemplo, stackoverflow.com/questions/17902492/… .
– Karl Knechtel
18 de febrero a las 6:27