Почему "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
блок выполняется.
Есть три распространенных способа правильного построения этого условия.
Используйте несколько
==
операторов для явной проверки каждого значения:if name == "Kevin" or name == "Jon" or name == "Inbar":
Создайте коллекцию допустимых значений (например, set, список или кортеж) и используйте
in
оператор для проверки принадлежности:if name in {"Kevin", "Jon", "Inbar"}:
Используйте
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