提问人:Kevin 提问时间:11/15/2013 最后编辑:wjandreaKevin 更新时间:5/11/2023 访问量:45143
为什么“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?
问:
我正在编写一个安全系统,拒绝未经授权的用户访问。
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 等于时才授予访问权限。我也尝试过相反的逻辑,但结果是一样的。name
if "Kevin" or "Jon" or "Inbar" == name
这个问题旨在作为这个非常常见问题的规范重复目标。还有另一个热门问题:如何根据单个值测试多个变量的相等性? 这有相同的基本问题,但比较目标被颠倒了。这个问题不应该作为该问题的重复来关闭,因为这个问题是 Python 的新手遇到的,他们可能难以将反向问题的知识应用于他们的问题。
对于 in
而不是 ==
,这里有解决方案: 如何测试列表中多个值的成员资格
答:
在许多情况下,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
有三种常见的方法可以正确构造此条件。
使用多个运算符显式检查每个值:
==
if name == "Kevin" or name == "Jon" or name == "Inbar":
编写有效值(例如集合、列表或元组)的集合,并使用运算符测试成员资格:
in
if name in {"Kevin", "Jon", "Inbar"}:
-
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
评论
("Kevin", "Jon", "Inbar")
{"Kevin", "Jon", "Inbar"}
a in {b, c, d}
a in (b, c, d)
frozenset
dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
简单的工程问题,让我们更进一步。
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')
为了安全起见,我还建议您不要对密码进行硬编码。
有 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.")
非空列表、集合、字符串等是可计算的,因此返回 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
方法
数据科学家如何处理这个问题
最简单的方法是消除对比较运算符的需要并使用列表。这在安全系统上看起来令人印象深刻,因为您学会了访问 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 键。如果要在不进行哈希处理的情况下有效地延迟加载用户凭据,则可以按如下方式对对象进行排序: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。
总结所有现有答案
(并补充我的一些观点)
解释:
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.")
除了已经提到的其他一些对海象操作员来说相当罕见的有用情况。这也往往也是一个有用的案例。
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
在 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.")
请注意,案例是使用 “组合”的,而不是 。这是一种特殊的语法:Python 不会首先尝试计算(不适用于字符串),而是以不同的方式解释整行,因为它以 .|
or
"Kevin" | "Jon" | "Inbar"
|
case
评论
x or y in z
x and y in z
x != y and z