Во многих случаях Python выглядит и ведет себя как естественный английский, но это тот случай, когда эта абстракция терпит неудачу. Люди могут использовать контекстные подсказки, чтобы определить, что «Джон» и «Инбар» являются объектами, соединенными с глаголом «равно», но интерпретатор Python более буквальный.
if name == "Kevin" or "Jon" or "Inbar":
логически эквивалентно:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Что для пользователя Боба эквивалентно:
if (False) or ("Jon") or ("Inbar"):
or
Оператор выбирает первый аргумент с положительным значением истинности :
if ("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
Для тех, кому может понадобиться доказательство, if a == b or c or d or e: ...
которое действительно разбирается таким образом. Встроенный ast
модуль дает ответ:
>>> import ast
>>> ast.parse("if a == b or c or d or e: ...")
<_ast.Module object at 0x1031ae6a0>
>>> ast.dump(_)
"Module(body=[If(test=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())]), body=[Expr(value=Ellipsis())], orelse=[])])"
>>>
Таким образом, test
из if
заявления выглядит следующим образом :
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
применяется к нескольким values
, а именно, a == b
и c
, d
и e
.