Почему `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.


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

  1. Используйте несколько операторов == для явной проверки каждого значения:
    if name == "Kevin" or name == "Jon" or name == "Inbar":

  2. Составьте последовательность допустимых значений и используйте оператор 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.