1. ホーム
  2. python

[解決済み] なぜ「a == x or y or z」は常に「真」と評価されるのですか?

2022-06-14 01:08:08

疑問点

不正なユーザがアクセスできないようにするセキュリティシステムを書いています。

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 という逆のロジックも試してみましたが、結果は同じでした。


ノート : この質問は、この非常に一般的な問題の正規の重複対象として意図されています。別の普及した質問がある 1つの値に対して複数の変数が等しいかどうかをテストする方法は? という、同じ基本的な問題を持つ質問もありますが、比較対象が逆になっています。この問題は Python の初心者が遭遇するもので、反転した問題からの知識を自分の問題に適用するのが難しいかもしれないので、この質問をその重複として閉じるべきではありません。

どのように解決するのですか?

多くの場合、Pythonは自然な英語のように見え、動作しますが、これはその抽象化が失敗する1つのケースです。人は文脈を手がかりに "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 "Jon":

また、"Jon"は正の真理値を持っているので if ブロックが実行されます。これが、与えられた名前に関係なく"Access granted"が出力される原因です。

この推論はすべて、式 if "Kevin" or "Jon" or "Inbar" == name .最初の値である "Kevin" は真なので if ブロックが実行されます。


この条件を適切に構築する一般的な方法は2つあります。

  1. 複数の == 演算子を使用して、各値に対して明示的にチェックします。

    if name == "Kevin" or name == "Jon" or name == "Inbar":
    
    
  2. 有効な値の集まり(例えばセット、リスト、タプル)を構成し、その中で in 演算子を使用してメンバーシップをテストします。

    if name in {"Kevin", "Jon", "Inbar"}:
    
    

一般に、2つのうち2番目が読みやすく、また速度も速いので好まれるはずです。

>>> 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("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 を 4 つの部分式に適用したものです。 a == b と単純な式 c , d そして e .