为什么“a==x或y或z”的计算结果总是为真?

2024-03-29 00:00:48 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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,但结果是一样的

Note: this question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.


Tags: ortheto用户namehelloaccessis
3条回答

if name == "Kevin" or "Jon" or "Inbar":中有3个条件检查

  • 姓名==“凯文”
  • “乔恩”
  • “Inbar”

这个if语句等价于

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

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

解决方案


您可以使用下面的任何一种方法

快速

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

简单的工程问题,让我们更进一步

In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False

但是,Python继承了C语言,将非零整数的逻辑值计算为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')

为了安全起见,我还建议你不要硬编码密码

在许多情况下,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运算符选择第一个带有正truth value的参数:

if "Jon":

由于“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

对于那些想要证明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应用于四个子表达式:比较a == b;和简单表达式cde

相关问题 更多 >