왜`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.

왜 이런 일이 발생합니까? nameKevin, Jon 또는 Inbar와 같을 때만 액세스 권한을 부여하겠다고 명시했습니다 . 나는 또한 반대 논리를 시도 if "Kevin" or "Jon" or "Inbar" == name했지만 결과는 동일합니다.


1
@ Jean-François 참고로 파이썬 룸에서이 질문과 그 속임수 대상에 대한 토론 이있었습니다 . 여기서 토론이 시작됩니다 . 폐쇄하고 싶은지 알고 있지만 최근에 게시물이 다시 열린 이유에 대해 알고 싶을 것 같습니다. 전체 공개 : 속임수 대상에 대한 답변의 작성자 인 Martijn은 아직 문제에 대해 차임 할 시간이 없었습니다.
— Andras Deak

Martijn 답변은 "자연어를 사용하지 마십시오"라고 설명하는 것이 훌륭합니다. 다른 사람들은 영광스러운 투표 시간이었습니다 ... 아래 답변은 이것을 반복합니다. 나를 위해 그것은 중복입니다. 하지만 Martijn이 재개 점을 선택해도 괜찮습니다.
— Jean-François Fabre

4
이 문제의 변형을 포함 x or y in z, x and y in z, x != y and z그리고 몇 가지 다른. 이 질문과 정확히 동일하지는 않지만 근본 원인은 모두 동일합니다. 누군가 자신의 질문이 이것의 중복으로 종결되었고 그것이 그들과 어떻게 관련이 있는지 확실하지 않은 경우를 지적하고 싶었습니다.
— Aran-Fey

답변:


153

대부분의 경우 Python은 자연스러운 영어처럼 보이고 동작하지만 이는 추상화가 실패하는 경우입니다. 사람들은 문맥 단서를 사용하여 "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"은 참이므로 if블록이 실행됩니다.


이 조건부를 적절하게 구성하는 두 가지 일반적인 방법이 있습니다.

  1. 여러 ==연산자를 사용 하여 각 값을 명시 적으로 확인합니다.
    if name == "Kevin" or name == "Jon" or name == "Inbar":

  2. 유효한 값의 시퀀스를 작성하고 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.


("Kevin", "Jon", "Inbar")세트 대신 튜플을 선택하는 특별한 이유가 {"Kevin", "Jon", "Inbar"} 있습니까?
— 인간

2
값이 모두 해시 가능한 경우 둘 다 작동하므로 실제로는 아닙니다. 집합 멤버십 테스트는 튜플 멤버십 테스트보다 big-O 복잡성이 더 좋지만 집합을 구성하는 것은 튜플을 구성하는 것보다 약간 비쌉니다. 이런 작은 컬렉션은 대체로 워시라고 생각합니다. timeit을 가지고 노는 것은 내 컴퓨터 a in {b, c, d}보다 약 두 배 빠릅니다 a in (b, c, d). 이것이 성능에 중요한 코드 조각인지 생각해 볼 것.
— Kevin 19

3
'if'절에서 'in'을 사용할 때 튜플 또는 목록? 멤버십 테스트를위한 세트 리터럴을 권장합니다. 내 게시물을 업데이트하겠습니다.
— Kevin 19

현대 파이썬에서는 세트가 상수라는 것을 인식하고 frozenset대신 상수로 만들 므로 구성 세트 오버 헤드가 없습니다. dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
— endolith

1

간단한 엔지니어링 문제입니다. 좀 더 자세히 살펴 보겠습니다.

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

드디어

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.