为什么“a == x 或 y 或 z”的计算结果总是为 True?我怎样才能将“a”与所有这些进行比较?

Why does "a == x or y or z" always evaluate to True? How can I compare "a" to all of those?

提问人:Kevin 提问时间:11/15/2013 最后编辑:wjandreaKevin 更新时间:5/11/2023 访问量:45143

问:

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

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.

为什么会这样?我已经明确表示,只有在 Kevin、Jon 或 Inbar 等于时才授予访问权限。我也尝试过相反的逻辑,但结果是一样的。nameif "Kevin" or "Jon" or "Inbar" == name


这个问题旨在作为这个非常常见问题的规范重复目标。还有另一个热门问题:如何根据单个值测试多个变量的相等性? 这有相同的基本问题,但比较目标被颠倒了。这个问题不应该作为该问题的重复来关闭,因为这个问题是 Python 的新手遇到的,他们可能难以将反向问题的知识应用于他们的问题。

对于 in 而不是 ==,这里有解决方案: 如何测试列表中多个值的成员资格

python boolean boolean-expression

评论

14赞 Aran-Fey 4/11/2019
此问题的变体包括 、 和其他一些变体。虽然与这个问题不完全相同,但所有这些问题的根本原因都是一样的。只是想指出这一点,以防有人将他们的问题作为重复的问题关闭并且不确定它与他们有什么关系。x or y in zx and y in zx != y and z

答:

229赞 9 revs, 7 users 37%Kevin #1

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

运算符选择第一个“真实”操作数,即满足 if 条件(或最后一个操作数,如果它们都不是“真实”的):or

if "Jon":

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

所有这些推理也适用于表达式 。第一个值 为 true,因此块执行。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"}:
    
  3. 使用 any()生成器表达式显式检查循环中的每个值:

    if any(name == auth for auth in ["Kevin", "Jon", "Inbar"]):
    

一般来说,应该首选第二种,因为它更易于阅读且速度更快:

>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
    setup="name='Inbar'")
0.0960568820592016
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.034957461059093475
>>> timeit.timeit('any(name == auth for auth in ["Kevin", "Jon", "Inbar"])',
    setup="name='Inbar'")
0.6511583919636905

对于那些可能想要证明确实像这样解析的人。内置模块提供了答案: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())]))

正如人们所看到的,它是应用于四个子表达式的布尔运算符:比较;和简单表达式 、 和 。ora == bcde

评论

1赞 Human 6/14/2019
选择元组而不是集合是否有特定的原因?("Kevin", "Jon", "Inbar"){"Kevin", "Jon", "Inbar"}
3赞 Kevin 6/14/2019
并非如此,因为如果值都是可哈希的,则两者都有效。集合成员资格测试比元组成员资格测试具有更好的 big-O 复杂性,但构造集合比构造元组要贵一些。我认为这在很大程度上是对像这样的小型收藏品的洗刷。玩timeit,大约是在我的机器上的两倍。如果这是一段对性能至关重要的代码,则需要考虑一些事情。a in {b, c, d}a in (b, c, d)
3赞 Kevin 6/14/2019
在“if”子句中使用“in”时是元组还是列表?建议为成员资格测试设置文本。我会更新我的帖子。
4赞 endolith 5/17/2020
在现代 Python 中,它识别出集合是一个常量,并使其成为一个常量,因此构造集开销不存在。frozensetdis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
0赞 TylerH 10/24/2022
FWIW:我认为你应该重新添加元组,因为这比集合更容易理解。
2赞 Ofer Rahat 5/17/2019 #2

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

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

但是,继承自 C 语言,Python 将非零整数的逻辑值评估为 True。

In [11]: if 3:
    ...:     print ("yey")
    ...:
yey

现在,Python 基于该逻辑构建,并允许您在整数上使用逻辑文字,例如 或 in 整数等

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')

为了安全起见,我还建议您不要对密码进行硬编码。

6赞 7u5h4r 6/24/2020 #3

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

由于将始终为 true,因此授予对任何用户的访问权限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.")
3赞 niamulbengali 11/30/2020 #4

非空列表、集合、字符串等是可计算的,因此返回 True。

因此,当你说:

a = "Raul"
if a == "Kevin" or "John" or "Inbar":
    pass

你实际上是在说:

if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
    pass

由于 “John” 和 “Inbar” 中至少有一个不是空字符串,因此整个表达式始终返回 True!

解决方案:

a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
    pass

或:

a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
    pass

评论

2赞 Antti Haapala -- Слава Україні 4/12/2021
否则很好,但“你实际上是在说:”是错误的,这不是工作方式。表达式的值是 ,而不是 。or"John"True
-4赞 vladusatii 12/8/2020 #5

方法

数据科学家如何处理这个问题

最简单的方法是消除对比较运算符的需要并使用列表。这在安全系统上看起来令人印象深刻,因为您学会了访问 ORM。

user = input("Enter name: ")

if user in {"Bob", "Kevin", "Joe"}:
   print("Access granted, " + str(user) + ".")
else:
   print("Access denied.")

或者,您可以类似于上面完全相同的代码,只需将注册用户列表放在他们自己的列表中即可:

user = input("Enter name: ")
users = {"Bob", "Kevin", "Joe", "a million more users if you like"}

if user in users:
   print("Access granted, " + str(user) + ".")
else:
   print("Access denied.")

如果您想在没有攻击风险的情况下安全地完成此协议,请设置双精度参数。这将检查您的 mini-ORM 的 和 name 字段,以及 or 键。如果要在不进行哈希处理的情况下有效地延迟加载用户凭据,则可以按如下方式对对象进行排序:firstlastpasswordsecret question

def lazy(i):
   j = 0 # For example
   while j < i:
      yield j
      j += 1

该循环将消耗产生的值,以节省系统的时间和能源:

然后,您可以使用迭代列表执行某些操作:

for j in lazy_range(10):
   do_something_here(j)

这个问题可以从任何角度来解决:内存管理、安全性,或者只是通过有机列表或打包的 ORM。

8赞 Deepthi Tabitha Bennet 2/20/2022 #6

总结所有现有答案

(并补充我的一些观点)

解释:

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.")
0赞 Thingamabobs 12/28/2022 #7

除了已经提到的其他一些对海象操作员来说相当罕见的有用情况。这也往往也是一个有用的案例。

def calc_value():
    return 43

if (v := calc_value()) == 43 and v > 42:
    print('happy short, efficient and readable code')

这之所以有效,是因为 if 语句的每个部分都是单独读取的。因此,执行并为其赋值,如果第一个失败,则命名空间中仍有 v 用于不同的条件或计算。(v := calc_value())v

1赞 Karl Knechtel 2/1/2023 #8

在 Python 3.10 及更高版本中使用 /matchcase

Python 3.10 为该语言添加了一种新语法。它被官方描述为“结构模式匹配”,但大多数人根据语法来称呼它:“/”。matchcase

我们可以将这种特殊语法用于示例,例如在问题中,通过创建一个与所有接受的用户名匹配的“大小写”,并使用“通配符”大小写代替 .因此:_else

name = input("Hello. Please enter your name: ")
match name:
    case "Kevin" | "Jon" | "Inbar":
        print("Access granted.")
    case _:
        print("Access denied.")

请注意,案例是使用 “组合”的,而不是 。这是一种特殊的语法:Python 不会首先尝试计算(不适用于字符串),而是以不同的方式解释整行,因为它以 .|or"Kevin" | "Jon" | "Inbar"|case