为什么`a == b或c或d`总是评估为True?

Kev*_*vin 96 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.")
Run Code Online (Sandbox Code Playgroud)

它按预期授予对授权用户的访问权限,但它也允许未经授权的用户访问!

Hello. Please enter your name:
Bob
Access granted.
Run Code Online (Sandbox Code Playgroud)

为什么会这样?我明确表示,只有在与nameKevin,Jon或Inbar相同时才授予访问权限.我也尝试过相反的逻辑if "Kevin" or "Jon" or "Inbar" == name,但结果是一样的.

Kev*_*vin 134

在许多情况下,Python的外观和行为与自然英语相似,但这是抽象失败的一种情况.人们可以使用上下文线索来确定"Jon"和"Inbar"是加入动词"equals"的对象,但Python解释器更具有文字意识.

if name == "Kevin" or "Jon" or "Inbar":
Run Code Online (Sandbox Code Playgroud)

在逻辑上等同于:

if (name == "Kevin") or ("Jon") or ("Inbar"):
Run Code Online (Sandbox Code Playgroud)

对于用户Bob来说,相当于:

if (False) or ("Jon") or ("Inbar"):
Run Code Online (Sandbox Code Playgroud)

or运营商选择以积极的第一个参数真值:

if ("Jon"):
Run Code Online (Sandbox Code Playgroud)

由于"Jon"具有正的真值,因此该if块执行.这就是导致"授予访问权限"被打印的原因,无论给出的名称如何.

所有这些推理也适用于表达式if "Kevin" or "Jon" or "Inbar" == name.第一个值为"Kevin"true,因此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
Run Code Online (Sandbox Code Playgroud)

  • 在现代Python中,它认识到集合是一个常量,并将其设为“冻结集”,因此不存在构造集合的开销。`dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))` (4认同)
  • [在“if”子句中使用“in”时的元组或列表?](/sf/ask/1775783621/) 建议使用集合文字进行成员资格测试。我会更新我的帖子。 (3认同)
  • 不是真的,因为如果值都是可散列的,两者都可以工作。集合成员测试比元组成员测试具有更好的 big-O 复杂度,但构造一个集合比构造一个元组要贵一点。我认为这主要是对像这样的小收藏品的清洗。在我的机器上玩 timeit,`a in {b, c, d}` 大约是 `a in (b, c, d)` 的两倍。如果这是一段对性能至关重要的代码,需要考虑一下。 (2认同)

Dee*_*net 8

总结所有现有答案

(并补充一些我的观点)

解释 :

if name == "Kevin" or "Jon" or "Inbar":
Run Code Online (Sandbox Code Playgroud)

逻辑上等价于:

if (name == "Kevin") or ("Jon") or ("Inbar"):
Run Code Online (Sandbox Code Playgroud)

对于用户 Bob 来说,这相当于:

if (False) or ("Jon") or ("Inbar"):
Run Code Online (Sandbox Code Playgroud)

注意:Python 将任何非零整数的逻辑值计算为True。因此,所有非空列表、集合、字符串等都是可计算的并返回True

运算or符选择第一个具有正真值的参数。

因此,“Jon”具有正真值并且 if 块执行,因为它现在相当于

if (False) or (True) or (True):
Run Code Online (Sandbox Code Playgroud)

这就是导致无论输入什么名称都会打印“授予访问权限”的原因。

解决方案:

解决方案 1:使用多个==运算符显式检查每个值

if name == "Kevin" or name == "Jon" or name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
Run Code Online (Sandbox Code Playgroud)

解决方案 2:组合有效值的集合(例如集合、列表或元组),并使用运算in符来测试成员资格(更快,首选方法)

if name in {"Kevin", "Jon", "Inbar"}:
    print("Access granted.")
else:
    print("Access denied.")
Run Code Online (Sandbox Code Playgroud)

或者

if name in ["Kevin", "Jon", "Inbar"]:
    print("Access granted.")
else:
    print("Access denied.")
Run Code Online (Sandbox Code Playgroud)

解决方案 3:使用基本(且不是很有效)的 if-elif-else结构

if name == "Kevin":
    print("Access granted.")
elif name == "Jon":
    print("Access granted.")
elif name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
Run Code Online (Sandbox Code Playgroud)


7u5*_*h4r 6

有 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.")
Run Code Online (Sandbox Code Playgroud)

由于elif "Jon"始终为真,因此授予任何用户访问权限

解决方案


您可以使用以下任意一种方法

快速地

if name in ["Kevin", "Jon", "Inbar"]:
    print("Access granted.")
else:
    print("Access denied.")
Run Code Online (Sandbox Code Playgroud)

慢的

if name == "Kevin" or name == "Jon" or name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
Run Code Online (Sandbox Code Playgroud)

缓慢 + 不必要的代码

if name == "Kevin":
    print("Access granted.")
elif name == "Jon":
    print("Access granted.")
elif name == "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
Run Code Online (Sandbox Code Playgroud)