Почему `a` ==b или c или d` всегда оценивают до Истины.

python boolean boolean-expression


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

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 , но результат тот же.





Answer 1 Kevin


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

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

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

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

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

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



Answer 2 user1854182


Простая инженерная проблема,давайте попробуем немного дальше.

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

Finally

In [4]: a==b or c or d
Out[4]: 3

Надлежащий способ написать это:

In [13]: if a in (b,c,d):
    ...:     print('Access granted')

В целях безопасности я бы также посоветовал вам не задавать пароли с жестким кодом.