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

2024-11-15 08:36:00
admin
原创
15
摘要:问题描述:我正在编写一个拒绝未经授权用户访问的安全系统。name = input("Hello. Please enter your name: ") if name == "Kevin" or "Jon" or "Inbar":...

问题描述:

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

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


这个问题旨在作为这个非常常见问题的典型重复目标。还有另一个流行问题如何测试多个变量与单个值的相等性?它具有相同的基本问题,但比较目标是相反的。这个问题不应该作为那个问题的重复而关闭,因为这个问题是 Python 新手遇到的,他们可能难以将反向问题中的知识应用到他们的问题中。

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


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

or运算符选择第一个为“真”的操作数,也就是满足if条件的操作数(如果所有操作数都为“真”,则选择最后一个操作数):

if "Jon":

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

所有这些推理也适用于表达式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"}:
  1. 使用生成器表达式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())]))

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

解决方案 2:

总结所有现有答案

(并补充一些我的观点)

解释 :

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:

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

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

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

解决方案 5:

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

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

  • 技术规格

  • 动机和理由(即为什么添加它,以及设计的灵感是什么)

  • 官方教程

我们可以使用这种特殊语法来举一个问题中的例子,通过创建一个与所有接受的用户名匹配的“案例”,并使用“通配符”_代替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

解决方案 6:

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

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

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

解决方案 7:

除了前面提到的海象运算符的一些其他相当罕见的有用情况之外,这也往往是一个有用的情况。

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 以用于不同的条件或计算。

解决方案 8:

方法

数据科学家如何解决这个问题

最简单的方法是消除对比较运算符的需求并使用列表。这在安全系统上看起来令人印象深刻,因为您学会了访问 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 是否存在firstlast名称字段,以及passwordsecret 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。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用