Вопрос-Ответ

Why does "a == x or y or z" always evaluate to True? How can I compare "a" to all of those?

Почему "a = = x, y или z" всегда принимает значение True? Как я могу сравнить "a" со всеми этими значениями?

Я пишу систему безопасности, которая запрещает доступ неавторизованным пользователям.

name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")

Он предоставляет доступ авторизованным пользователям, как и ожидалось, но также допускает неавторизованных пользователей!

Hello. Please enter your name: Bob
Access granted.

Почему это происходит? Я прямо заявил, что предоставлять доступ нужно только тогда, когда name равно Kevin, Jon или Inbar. Я также пробовал противоположную логику, if "Kevin" or "Jon" or "Inbar" == name но результат тот же.


Этот вопрос предназначен как каноническая дублирующая цель этой очень распространенной проблемы. Есть еще один популярный вопрос. Как проверить несколько переменных на равенство одному значению? у этого есть та же фундаментальная проблема, но цели сравнения поменялись местами. Этот вопрос не следует закрывать как дубликат предыдущего, поскольку с этой проблемой сталкиваются новички в Python, у которых могут возникнуть трудности с применением знаний из перевернутого вопроса к своей проблеме.

Для in вместо == здесь есть решения: Как проверить принадлежность нескольких значений к списку

Переведено автоматически
Ответ 1

Во многих случаях Python выглядит и ведет себя как естественный английский, но это один из случаев, когда эта абстракция дает сбой. Люди могут использовать контекстные подсказки, чтобы определить, что "Jon" и "Inbar" - это объекты, соединенные с глаголом "equals", но интерпретатор Python настроен более буквально.

if name == "Kevin" or "Jon" or "Inbar":

логически эквивалентно:

if (name == "Kevin") or ("Jon") or ("Inbar"):

Что для пользователя Bob эквивалентно:

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

or Оператор выбирает первый операнд, который является "истинным", т.Е. Который удовлетворял бы if условию (или последнему, если ни одно из них не является "истинным"):

if "Jon":

Поскольку "Jon" соответствует действительности, if выполняется блок. Это то, что приводит к печати "Доступ предоставлен" независимо от указанного имени.

Все эти рассуждения также применимы к выражению if "Kevin" or "Jon" or "Inbar" == name. первое значение, "Kevin" , равно true, поэтому if блок выполняется.


Есть три распространенных способа правильного построения этого условия.


  1. Используйте несколько == операторов для явной проверки каждого значения:


    if name == "Kevin" or name == "Jon" or name == "Inbar":


  2. Создайте коллекцию допустимых значений (например, set, список или кортеж) и используйте in оператор для проверки принадлежности:


    if name in {"Kevin", "Jon", "Inbar"}:


  3. Используйте any() и выражение генератора для явной проверки каждого значения в цикле:


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


В целом следует предпочесть второе, поскольку оно легче читается, а также быстрее:

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

Для тех, кому могут понадобиться доказательства того, что if a == b or c or d or e: ... действительно анализируется подобным образом. Встроенный ast модуль предоставляет ответ:

>>> 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())]))

Как можно видеть, это логический оператор, or применяемый к четырем подвыражениям: comparison a == b; и простым выражениям c, d и e.

Ответ 2

Обобщая все существующие ответы

(И добавляю несколько своих замечаний)

Объяснение :

if name == "Kevin" or "Jon" or "Inbar":

логически эквивалентно:

if (name == "Kevin") or ("Jon") or ("Inbar"):

Что для пользователя Bob эквивалентно:

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

ПРИМЕЧАНИЕ: Python оценивает логическое значение любого ненулевого целого числа как True. Следовательно, все непустые списки, наборы, строки и т.д. Поддаются оценке и возвращают True

or Оператор выбирает первый аргумент с положительным значением истинности.

Следовательно, "Jon" имеет положительное значение истинности, и выполняется блок if, поскольку теперь он эквивалентен

if (False) or (True) or (True):

Именно это приводит к печати "Доступ предоставлен" независимо от введенного имени.

Решения :

Решение 1: Используйте несколько == операторов для явной проверки каждого значения

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

Решение 2 : Создайте коллекцию допустимых значений (например, набор, список или кортеж) и используйте in оператор для проверки членства (более быстрый и предпочтительный метод)

if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")

или

if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")

Решение 3: Использовать базовую (и не очень эффективную) 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.")
Ответ 3

Есть 3 проверки условий в if name == "Kevin" or "Jon" or "Inbar":


  • имя == "Кевин"

  • "Джон"

  • "Inbar"

и этот оператор if эквивалентен

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

Поскольку elif "Jon" всегда будет true, поэтому доступ предоставляется любому пользователю

Решение


Вы можете использовать любой из приведенных ниже методов

Быстро

if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")

Медленно

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

Медленный + ненужный код

if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Ответ 4

Непустые списки, наборы, строки и т.д. Поддаются оценке и, следовательно, возвращают True.

Поэтому, когда вы говорите:

a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass

Вы на самом деле говорите:

if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass

Поскольку по крайней мере одно из "John" и "Inbar" не является пустой строкой, все выражение всегда возвращает True!

Решение:

a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass

или:

a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
python