`a == bまたはcまたはd`が常にTrueと評価されるのはなぜですか?


108

権限のないユーザーのアクセスを拒否するセキュリティシステムを作成しています。

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が、結果は同じです。


1
@Jean-FrançoisFYIこの質問とそのだまされたターゲットについての議論 python部屋の前半でありました。議論はここから始まります。閉鎖したいかどうかはわかりましたが、投稿が最近再開された理由を知りたいと思っているかもしれません。完全な開示:だまされたターゲットに関する回答の作成者であるMartijnは、まだこの問題について話し合う時間はありません。
Andras Deak

Martijnの回答は、「自然言語を使用しない」で説明するのに非常に優れています。他の人もそうです...これらは素晴らしい投票期間でした...以下の回答はこれを繰り返すだけです。私にとってそれは複製です。しかし、Martijnが再開することを選択したとしても、私は気にしません。
ジャン=フランソワ・ファーブル

4
この問題のバリエーションが含まれx or y in zx and y in zx != y and zおよびいくつかの他。この質問とまったく同じではありませんが、根本的な原因はすべて同じです。誰かがこれの重複として質問を閉じた場合に備えて、それがどのように彼らに関連しているかわからない場合に備えて、それを指摘したかっただけです。
Aran-Fey

回答:


151

多くの場合、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ブロックが実行されます。これが、指定された名前に関係なく「アクセス許可」が印刷される原因です。

この推論はすべて式にも当てはまります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つ目が推奨されます。

>>> 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=[])])"
>>>

だから、testifこのようなステートメントのルックス:

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())
 ]
)

1が見ることができるように、それはブール演算子だor複数に適用されるvalues、すなわち、a == bcde


("Kevin", "Jon", "Inbar")セットではなくタプルを選択する特別な理由はあります{"Kevin", "Jon", "Inbar"} か?
人間

2
値がすべてハッシュ可能であればどちらも機能するため、実際にはそうではありません。セットメンバーシップテストは、タプルメンバーシップテストよりもBig-Oの複雑度が優れていますが、セットの作成はタプルの作成よりも少しコストがかかります。これは、主にこのような小規模なコレクションのウォッシュだと思います。timeitをいじってみると、私のマシンのa in {b, c, d}約2倍の速さa in (b, c, d)です。これがパフォーマンス上重要なコードであるかどうかについて検討する必要があります。
Kevin

3
「if」句で「in」を使用する場合のタプルまたはリスト?メンバーシップテストにはリテラルを設定することをお勧めします。投稿を更新します。
Kevin

最新のPythonでは、セットが定数であることを認識し、それをa frozensetにしているため、セットのオーバーヘッドの構築はありません。dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
内部石

1

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

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

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


1

3つの状態チェックがあります if name == "Kevin" or "Jon" or "Inbar":

  • 名前== "ケビン"
  • 「ジョン」
  • 「インバー」

このifステートメントは次と同等です

if name == "Kevin":
    print("Access granted.")
elif "Jon":
    print("Access granted.")
elif "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

のでelif "Jon"、任意のユーザーへのアクセスが許可されるので、常にtrueになります

解決


以下のいずれかの方法を使用できます

速い

if name in ["Kevin", "Jon", "Inbar"]:
    print("Access granted.")
else:
    print("Access denied.")

スロー

if name == "Kevin" or name == "Jon" or name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

遅い+不要なコード

if name == "Kevin":
    print("Access granted.")
elif name == "Jon":
    print("Access granted.")
elif name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.