web-dev-qa-db-ja.com

なぜ `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がKevin、Jon、またはInbarに等しい場合にのみアクセスを許可することを明確に述べました。私も反対の論理を試してみました、if "Kevin" or "Jon" or "Inbar" == name、しかし結果は同じです。

96
Kevin

多くの場合、Pythonは自然な英語のように見え、動作しますが、これはその抽象化が失敗する1つのケースです。人々はコンテキスト手がかりを使用して、「Jon」動詞は「等しい」が、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"はtrueであるため、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
132
Kevin

単純なエンジニアリングの問題です。もう少し簡単に説明しましょう。

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')

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

0
user1854182