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

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

사람이 볼 수 있듯이, 상기 부울 연산자의 or여러 적용 values, 즉 a == bc, de.


("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.