为什么“a == x or y or z”总是计算结果为 True?我如何将“a”与所有这些进行比较?
- 2024-11-15 08:36:00
- admin 原创
- 14
问题描述:
我正在编写一个拒绝未经授权用户访问的安全系统。
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
块执行。
有三种常见的方法可以正确构造此条件。
使用多个
==
运算符来明确检查每个值:
if name == "Kevin" or name == "Jon" or name == "Inbar":
组成有效值的集合(例如集合,列表或元组),并使用
in
运算符测试成员资格:
if name in {"Kevin", "Jon", "Inbar"}:
使用生成器表达式
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
;和简单表达式c
、d
和e
。
解决方案 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 是否存在first
和last
名称字段,以及password
或secret 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。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件