为什么“a == x or y or z”的计算结果总是为 True?我如何将“a”与所有这些进行比较?

新手上路,请多包涵

我正在编写一个拒绝未经授权用户访问的安全系统。

 name = input("Hello. Please enter your name: ")
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 ,但结果是一样的。


这个问题旨在作为这个非常常见问题的规范重复目标。还有另一个热门问题 How to test multiple variables for equality against a single value? 具有相同的基本问题,但比较目标相反。这个问题不应该作为那个问题的重复而关闭,因为这个问题是 Python 新手遇到的,他们可能难以将反向问题中的知识应用于他们的问题。

原文由 Kevin 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 373
2 个回答

在许多情况下,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":

  1. 组成有效值的集合(例如集合、列表或元组),并使用 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("a == b or c or d or e", "<string>", "eval")
<ast.Expression object at 0x7f929c898220>
>>> print(ast.dump(_, indent=4))
Expression(
    body=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 应用于四个子表达式: comparison a == b ;和简单的表达式 cde

原文由 Kevin 发布,翻译遵循 CC BY-SA 4.0 许可协议

总结所有现有答案

(并添加一些我的观点)

解释 :

 if name == "Kevin" or "Jon" or "Inbar":

在逻辑上等同于:

 if (name == "Kevin") or ("Jon") or ("Inbar"):

对于用户 Bob,这相当于:

 if (False) or ("Jon") or ("Inbar"):

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

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

因此,“Jon”有一个正的真值并且 if 块执行,因为它现在等同于

if (False) or (True) or (True):

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

解决方案:

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

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

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

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

或者

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

解决方案 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.")

原文由 Deepthi Tabitha Bennet 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题