¿Por qué "a == x o y o z" ​​siempre se evalúa como Verdadero? ¿Cómo puedo comparar "a" con todos esos?

Resuelto Kevin asked hace 11 años • 8 respuestas

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 permite la entrada a usuarios no autorizados!

Hello. Please enter your name: Bob
Access granted.

¿Por qué ocurre esto? He dicho claramente que solo se otorgará acceso cuando namesea igual a Kevin, Jon o Inbar. También probé la lógica opuesta, if "Kevin" or "Jon" or "Inbar" == namepero 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 la igualdad de múltiples variables con un solo valor? eso tiene el mismo problema fundamental, pero los objetivos de comparación están invertidos. Esta pregunta no debe cerrarse como un duplicado de aquella, ya que los recién llegados a Python encuentran este problema y pueden tener dificultades para aplicar el conocimiento de la pregunta invertida a su problema.

En inlugar de ==, hay soluciones aquí: Cómo probar la pertenencia de múltiples valores en una lista

Kevin avatar Nov 15 '13 20:11 Kevin
Aceptado

En muchos casos, Python se ve y se comporta como el inglés natural, pero este es un caso en el que esa 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, equivale a:

if (False) or ("Jon") or ("Inbar"):

El oroperador elige el primer operando que es "verdadero" , es decir, el que cumpliría una ifcondición (o el último, si ninguno de ellos es "verdadero"):

if "Jon":

Como "Jon" es veraz, el ifbloque se ejecuta. 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 verdadero, por lo que ifse ejecuta el bloque.


Hay tres formas comunes de construir correctamente este condicional.

  1. Utilice varios ==operadores para comprobar explícitamente cada valor:

    if name == "Kevin" or name == "Jon" or name == "Inbar":
    
  2. Cree una colección de valores válidos (un conjunto, una lista o una tupla, por ejemplo) y utilice el inoperador para probar la membresía:

    if name in {"Kevin", "Jon", "Inbar"}:
    
  3. Utilice any()y una expresión generadora para comparar explícitamente cada valor en un bucle:

    if any(name == auth for auth in ["Kevin", "Jon", "Inbar"]):
    

En general, se debería 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.0960568820592016
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.034957461059093475
>>> timeit.timeit('any(name == auth for auth in ["Kevin", "Jon", "Inbar"])',
    setup="name='Inbar'")
0.6511583919636905

Para aquellos que quieran pruebas de que if a == b or c or d or e: ...realmente se analizan así. El módulo incorporado astproporciona 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 oraplicado a cuatro subexpresiones: comparación a == b; y expresiones simples c, d, y e.

Kevin avatar Nov 15 '2013 13:11 Kevin

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, equivale 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. no vacíos son evaluables y devuelvenTrue

El oroperador 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: utilice varios ==operadores para comparar explícitamente cada valor

if name == "Kevin" or name == "Jon" or name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

Solución 2: cree una colección de valores válidos (un conjunto, una lista o una tupla, por ejemplo) y utilice el inoperador 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: utilice la estructura básica (y no muy eficiente) if-elif-else

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 avatar Feb 19 '2022 18:02 Deepthi Tabitha Bennet

Hay 3 controles de condición enif name == "Kevin" or "Jon" or "Inbar":

  • nombre == "Kevin"
  • "Jon"
  • "En el bar"

y esta declaración si es equivalente a

if name == "Kevin":
    print("Access granted.")
elif "Jon":
    print("Access granted.")
elif "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

Puesto que elif "Jon"siempre será cierto, por lo que se concede acceso a cualquier usuario.

Solución


Puede utilizar cualquiera de los métodos siguientes

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.")
7u5h4r avatar Jun 23 '2020 23:06 7u5h4r

Las listas, conjuntos, cadenas, etc. no vacíos son evaluables y, por lo tanto, devuelven Verdadero.

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 Verdadero.

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
niamulbengali avatar Nov 30 '2020 09:11 niamulbengali

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 número 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, etc.

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 le sugiero que no codifique contraseñas.

Ofer Rahat avatar May 17 '2019 12:05 Ofer Rahat

Usando match/ caseen Python 3.10 y superior

Python 3.10 agrega una nueva sintaxis al lenguaje. Se describe oficialmente como "coincidencia de patrones estructurales", pero la mayoría de la gente lo llama según la sintaxis: " match/ case".

  • Especificación técnica
  • Motivación y justificación (es decir, por qué se agregó y qué inspiró el diseño)
  • tutorial oficial

Podemos usar esta sintaxis especial para un ejemplo como en la pregunta, creando un "caso" que coincida con todos los nombres de usuario aceptados y usando el caso "comodín" _en lugar del else. De este modo:

name = input("Hello. Please enter your name: ")
match name:
    case "Kevin" | "Jon" | "Inbar":
        print("Access granted.")
    case _:
        print("Access denied.")

Tenga en cuenta que los casos se "combinan" usando |, no or. Esta es una sintaxis especial: Python no intenta calcular "Kevin" | "Jon" | "Inbar"primero ( |no funciona con cadenas), sino que interpreta la línea completa de manera diferente porque comienza con case.

Karl Knechtel avatar Feb 01 '2023 09:02 Karl Knechtel