8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

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

Afz 2月前

73 0

我正在编写一个拒绝未经授权用户访问的安全系统。name = input(\'Hello. Please enter your name:\')if name == \'Kevin\' or \'Jon\' or \'Inbar&quo...

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

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


This question is intended as the canonical duplicate target of this very common problem. There is another popular question 如何测试多个变量与单个值的相等性? 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.

For in instead of == , there are solutions here: 如何测试列表中多个值的成员资格

帖子版权声明 1、本帖标题:为什么 'a == x or y or z' 总是计算结果为 True?我如何将 'a' 与所有这些进行比较?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Afz在本站《if-statement》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 我不同意不应将此帖子作为重复帖子关闭的说法。比较可能会颠倒过来,但那无关紧要——问题和解决方案是相同的。正如帖子的修订历史所示,许多用户(包括金牌持有者和钻石版主)似乎都同意这一点。

  • 在许多情况下,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 运算符 选择第一个 “真”的 ,也就是说,它将 would satisfy an if condition (如果所有操作数都不是“真”,则选择最后一个操作数):

    if "Jon":
    

    由于 \'Jon\' 为真,因此 if 执行该块。这就是导致无论给出什么名称都会打印 \'Access grant\' 的原因。

    所有这些推理也适用于表达式 if "Kevin" or "Jon" or "Inbar" == name 。第一个值 "Kevin" 是真,因此该 if 块执行。


    有三种常见的方法可以正确构造此条件。

    1. p7

      if name == "Kevin" or name == "Jon" or name == "Inbar":
    2. p8

      if name in {"Kevin", "Jon", "Inbar"}:
    3. p9

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

    可以看出,它是 or 应用于四个子表达式的布尔运算符:比较 a == b ;和简单表达式 c , d ,以及 e .

  • Meri 2月前 0 只看Ta
    引用 4

    选择元组 (\'Kevin\', \'Jon\', \'Inbar\') 而不是集合 {\'Kevin\', \'Jon\', \'Inbar\'} 是否有特定原因?

  • 不是,因为如果值都是可哈希的,那么两者都可以工作。集合成员测试比元组成员测试具有更好的大 O 复杂度,但构造集合比构造元组要昂贵一些。我认为对于像这样的小集合来说,这基本上是无用的。使用 timeit 后,在我的计算机上,a in {b, c, d} 的速度大约是 a in (b, c, d) 的两倍。如果这是一段性能至关重要的代码,那么需要考虑一下。

  • Bron 2月前 0 只看Ta
    引用 6

    在“if”子句中使用“in”时,元组还是列表?建议使用集合文字进行成员资格测试。我会更新我的帖子。

  • 在现代 Python 中,它识别出集合是一个常量,并将其变为一个冻结集,因此不存在构造集合的开销。dis.dis(compile(\'1 in {1, 2, 3}\', '', '评估'))

  • 总结所有现有答案

    (并补充一些我的观点)

    解释 :

    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.")
    
  • 有 3 个条件检查 if name == "Kevin" or "Jon" or "Inbar":

    • 姓名 == \'Kevin\'
    • \“乔恩\”
    • '因巴尔'

    这个 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.")
    
  • 非空列表、集合、字符串等是可评估的,因此返回 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
    
  • 否则很好,但“您实际上是在说:\”是错误的,这不是 or 的工作方式。表达式的值是“John\”,而不是 True。

  • 在 Python 3.10 及更高版本中 match <使用code>/ case /

    Python 3.10 添加了一个新语法 。官方将其描述为“结构模式匹配”,但大多数人按照语法来称呼它:“ match / case ”。

    • 技术规格
    • 动机和理由 (即为什么添加它,以及设计的灵感是什么)
    • 官方教程

    我们可以使用这种特殊语法来举一个类似问题的例子,通过创建一个与所有接受的用户名匹配的 \'case\',并使用 \'wildcard\' _ 代替 else 。因此:

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

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

  • 简单的工程问题,我们再简单一点吧。

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

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

  • 海象运算符的 一些其他相当罕见的 ,这也往往是一个有用的情况。

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

    这是因为 if-statement 都是单独读取的。因此, (v := calc_value()) 将执行并分配一个值 v ,如果第一个失败,您仍然可以在命名空间中使用 v 来处理不同的条件或计算。

返回
作者最近主题: