Python 为什么'a==x或y或z'的计算结果总是为真?
我正在编写一个安全系统,拒绝未经授权的用户访问Python 为什么'a==x或y或z'的计算结果总是为真?,python,boolean,boolean-expression,Python,Boolean,Boolean Expression,我正在编写一个安全系统,拒绝未经授权的用户访问 name=input(“您好,请输入您的姓名:”) 如果名称==“Kevin”或“Jon”或“Inbar”: 打印(“已授予访问权限”) 其他: 打印(“访问被拒绝”) 它按预期授予授权用户访问权限,但也允许未经授权的用户进入 你好。请输入您的姓名:鲍勃 允许访问。 为什么会发生这种情况?我已经明确声明,只有当name等于Kevin、Jon或Inbar时,才允许访问。我也尝试过相反的逻辑,如果“Kevin”或“Jon”或“Inbar”==nam
name=input(“您好,请输入您的姓名:”)
如果名称==“Kevin”或“Jon”或“Inbar”:
打印(“已授予访问权限”)
其他:
打印(“访问被拒绝”)
它按预期授予授权用户访问权限,但也允许未经授权的用户进入
你好。请输入您的姓名:鲍勃
允许访问。
为什么会发生这种情况?我已经明确声明,只有当name
等于Kevin、Jon或Inbar时,才允许访问。我也尝试过相反的逻辑,如果“Kevin”或“Jon”或“Inbar”==name
,但结果是一样的
注意:此问题旨在作为这一常见问题的标准重复目标。还有一个流行的问题也有同样的基本问题,但比较目标是相反的。此问题不应与此问题重复,因为Python新手可能会遇到此问题,他们可能很难将反向问题中的知识应用到他们的问题中。
在许多情况下,Python的外观和行为类似于自然英语,但这是抽象失败的一种情况。人们可以使用上下文线索来确定“Jon”和“Inbar”是连接到动词“equals”的对象,但Python解释器更注重文字
if name == "Kevin" or "Jon" or "Inbar":
逻辑上等同于:
if (name == "Kevin") or ("Jon") or ("Inbar"):
if (False) or ("Jon") or ("Inbar"):
对于用户Bob,这相当于:
if (name == "Kevin") or ("Jon") or ("Inbar"):
if (False) or ("Jon") or ("Inbar"):
或
运算符选择第一个带正数的参数:
由于“Jon”具有正真值,因此执行if
块。这就是为什么不管给定的名称如何,都会打印“已授予访问权限”
如果“Kevin”或“Jon”或“Inbar”==name,所有这些推理也适用于表达式。第一个值,“Kevin”
,为true,因此执行if
块
有两种常见的方法可以正确构造此条件
使用多个==
运算符显式检查每个值:
if name == "Kevin" or name == "Jon" or name == "Inbar":
组成有效值的集合(例如集合、列表或元组),并使用in
运算符测试成员资格:
if name in {"Kevin", "Jon", "Inbar"}:
一般而言,应优先选择第二种,因为它更容易阅读,也更快:
>>> import timeit
>>> timeit.timeit('name == "Kevin" or name == "Jon" or name == "Inbar"',
setup="name='Inbar'")
0.4247764749999945
>>> timeit.timeit('name in {"Kevin", "Jon", "Inbar"}', setup="name='Inbar'")
0.18493307199999265
对于那些想要证明a==b或c或d或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())]))
导入ast
>>>ast.parse(“a==b或c或d或e”,“eval”)
>>>打印(ast.dump(u,缩进=4))
表情(
body=BoolOp(
op=或(),
价值观=[
比较(
左=名称(id='a',ctx=Load()),
老年退休金=[
Eq()],
比较器=[
名称(id='b',ctx=Load())],
名称(id='c',ctx=Load()),
名称(id='d',ctx=Load()),
名称(id='e',ctx=Load())])
可以看出,它是应用于四个子表达式的布尔运算符
或
:比较a==b
;和简单的表达式c
,d
,和e
,简单的工程问题,让我们再简单一点
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
但是,Python继承了C语言,将非零整数的逻辑值计算为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')
为了安全起见,我还建议您不要硬编码密码。如果name==“Kevin”或“Jon”或“Inbar”,则在
中有3个条件检查:
- 姓名==“凯文”
- “乔恩”
- “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.")
非空列表、集合、字符串等是可计算的,因此返回True。
因此,当你说:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
你实际上是在说:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
因为“John”和“Inbar”中至少有一个不是空字符串,所以整个表达式始终返回True
解决方案:
或:
方法
数据科学家如何处理这个问题
最简单的方法是不需要比较运算符,而是使用列表。这在安全系统上看起来令人印象深刻,因为您学会了访问ORMs
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.")
如果希望安全地完成此协议而不存在攻击风险,请设置双参数。这将检查迷你ORM中的第一个
和最后一个
名称字段,以及密码
或秘密问题
密钥。如果要在不进行散列的情况下高效地延迟加载用户凭据,可以按如下方式对对象进行排序:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
这个问题可以从任何角度来解决:内存管理、安全性,或者仅仅通过一个有机列表或打包的ORM
希望这能有所帮助。@Jean François FYI早些时候在python会议室讨论了这个问题及其dupe目标。我知道如果你想关闭它,但我想你可能想知道该帖子最近重新开放的原因。充分披露:Martijn,关于dupe target的答案的作者还没有来得及插话。Martijn的答案很好地解释了“不要使用自然语言”,其他人,嗯。。。那是辉煌的投票时代。。。下面的答案只是重复了这一点。对我来说,它是复制品。但是如果Martijn选择重新打开,我不介意。这个问题的变体包括x或y在z
,x和y在z
,x=
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
for j in lazy_range(10):
do_something_here(j)