Почему `a == b или c или d` всегда оценивают значение True?
Я пишу систему безопасности, которая запрещает доступ неавторизованным пользователям.
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.")
Он предоставляет доступ авторизованным пользователям, как и ожидалось, но также позволяет неавторизованным пользователям!
Hello. Please enter your name:
Bob
Access granted.
Почему это происходит? Я прямо заявил, что предоставлять доступ можно только тогда, когда name
совпадает с Кевином, Джоном или Инбаром. Я также попробовал противоположную логику, if "Kevin" or "Jon" or "Inbar" == name
, но результат тот же.
Ответы
Ответ 1
Во многих случаях Python выглядит и ведет себя как естественный английский, но это один из случаев, когда эта абстракция терпит неудачу. Люди могут использовать контекстные ключи, чтобы определить, что "Jon" и "Inbar" являются объектами, соединенными с глаголом "равно", но интерпретатор Python более буквально настроен.
if name == "Kevin" or "Jon" or "Inbar":
логически эквивалентно:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Что для пользователя Bob эквивалентно:
if (False) or ("Jon") or ("Inbar"):
Оператор or
выбирает первый аргумент с положительным значением истинности:
if ("Jon"):
А так как "Jon" имеет положительное значение истинности, блок if
выполняется. Именно поэтому "Доступ предоставлен" печатается независимо от заданного имени.
Все эти рассуждения также применимы к выражению, if "Kevin" or "Jon" or "Inbar" == name
. первое значение, "Kevin"
, истинно, поэтому выполняется блок if
.
Есть два распространенных способа правильно построить это условие.
-
Используйте несколько операторов ==
для явной проверки каждого значения:
if name == "Kevin" or name == "Jon" or name == "Inbar":
-
Составьте последовательность допустимых значений и используйте оператор in
для проверки членства:
if name in {"Kevin", "Jon", "Inbar"}:
В целом из двух предпочтительнее второе, так как оно легче для чтения и быстрее:
>>> 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
Ответ 2
Простая инженерная задача, пусть просто это немного дальше.
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
Но, унаследованный от языка C, Python оценивает логическое значение ненулевого целого числа как True.
In [11]: if 3:
...: print ("yey")
...:
yey
Теперь Python основывается на этой логике и позволяет вам использовать логические литералы, такие как или целые числа, и так
In [9]: False or 3
Out[9]: 3
в заключение
In [4]: a==b or c or d
Out[4]: 3
Правильный способ написать это будет:
In [13]: if a in (b,c,d):
...: print('Access granted')
В целях безопасности я бы также посоветовал вам не кодировать пароли жестко.
Ответ 3
Вы должны изменить строку, if name == "Kevin" or "Jon" or "Inbar":
на любую из нижеуказанных.
if name in {"Kevin","Jon","Inbar"}:
if (name=="Kevin") or (name =="Jon") or (name=="Inbar"):
Вещество, которое вы написали, оценивается как (name=="Kevin") or ("Jon") or ("Inbar")
которое всегда возвращает True
.