Python 为什么'a==x或y或z'的计算结果总是为真?

Python 为什么'a==x或y或z'的计算结果总是为真?,python,boolean,boolean-expression,Python,Boolean,Boolean Expression,我正在编写一个安全系统,拒绝未经授权的用户访问 name=inputHello。请输入您的姓名: 如果name==凯文、乔恩或英巴: 已授予打印访问权限。 其他: 打印访问被拒绝。 它按预期授予授权用户访问权限,但也允许未经授权的用户进入 你好请输入您的姓名:鲍勃 允许访问。 为什么会发生这种情况?我已经明确声明,只有当名字等于Kevin、Jon或Inbar时,才允许访问。我也尝试过相反的逻辑,如果Kevin或Jon或Inbar==name,但结果是一样的 注意:这个问题旨在作为这个非常常见问题

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

name=inputHello。请输入您的姓名: 如果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"):
or运算符选择第一个参数为正:

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

正如我们所看到的,它是布尔运算符或应用于四个子表达式:比较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个条件检查:

name==凯文 乔恩 英巴 这个if语句等价于

if name == "Kevin":
    print("Access granted.")
elif "Jon":
    print("Access granted.")
elif "Inbar":
    print("Access granted.")
else:
    print("Access denied.")
由于elif Jon始终为真,因此授予任何用户访问权限

解决方案 您可以使用下面的任何一种方法

快速

慢+不必要的代码

非空列表、集合、字符串等是可计算的,因此返回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!=y和z以及其他一些。虽然不完全是
与这个问题相同,所有这些问题的根本原因都是相同的。我只是想指出一点,以防有人把他们的问题作为这个问题的副本关闭,并且不确定这与他们有什么关系。@Jean Françoisfare所以你又把这个问题作为其中的副本关闭了?尽管另一种方法非常令人困惑,因为很少需要将几个变量与一个值进行比较,尽管另一种方法更为常见。是否有特定的原因选择元组Kevin,Jon,Inbar而不是集合{Kevin,Jon,Inbar}?不是真的,因为如果值都是可散列的,这两种方法都有效。集合成员关系测试比元组成员关系测试具有更好的大O复杂性,但构造集合比构造元组稍微贵一些。我认为这主要是对像这样的小收藏品的洗礼。在玩timeit时,{b,c,d}中的a速度大约是我机器上b,c,d中a速度的两倍。如果这是一段性能关键的代码,需要考虑一些问题。建议为成员资格测试设置文本。我将更新我的帖子。在现代Python中,它认识到集合是一个常量,并将其改为一个冻结集,因此不存在构造集合的开销。dis.discompile1在{1,2,3},'eval'@Kevin请接受这个好的,否则你实际上是在说:是错的,这不是正确的方法。表达式的值是John,而不是True。
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
    pass
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.")
def lazy(i):
   j = 0 # For example
   while j < i:
      yield j
      j += 1
for j in lazy_range(10):
   do_something_here(j)