なぜ `a ==b or c or d` は常に True に評価されるのか?

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 がKevin、Jon、またはInbarと等しい場合にのみアクセスを許可することを明言しました。 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" はtrueなので、 if ブロックが実行されます。


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

  1. 複数の == 演算子を使用して、各値を明示的にチェックします。
    if name == "Kevin" or name == "Jon" or name == "Inbar":

  2. 有効な値のシーケンスを作成し、 in 演算子を使用してメンバーシップをテストします。
    if name in {"Kevin", "Jon", "Inbar"}:

一般的には、2つのうち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



Answer 2 user1854182


単純な工学的な問題、もう少し単純化してみましょう。

In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False

しかし、C言語から継承されたPythonは、0ではない整数の論理値を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')

安全のためにも、パスワードをハードコード化しないことをお勧めします。