python—为什么'a==x或y或z'的计算结果总是为true?

fbcarpbf  于 2021-08-20  发布在  Java
关注(0)|答案(5)|浏览(798)

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

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 等于凯文、乔恩或英巴。我也试过相反的逻辑, if "Kevin" or "Jon" or "Inbar" == name ,但结果是一样的。
注意:这个问题旨在作为这个非常常见问题的标准复制目标。还有一个流行的问题,如何针对单个值测试多个变量?这有着同样的根本问题,但比较目标是相反的。这个问题不应该重复这个问题,因为这个问题是python新手遇到的,他们可能很难将反向问题中的知识应用到他们的问题中。

dxpyg8gm

dxpyg8gm1#

在许多情况下,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 "Jon":

既然“jon”有一个积极的真值 if 块执行。这就是为什么不管给定的名称如何,都会打印“已授予访问权限”。
所有这些推理也适用于表达式 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"}:

一般而言,应优先选择第二种,因为它更容易阅读,也更快:

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

对于那些可能需要证据的人 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 .

oknrviil

oknrviil2#

中有3个条件检查 if name == "Kevin" or "Jon" or "Inbar": 姓名==“凯文”
“乔恩”
“inbar”
这个if语句等价于

if name == "Kevin":
    print("Access granted.")
elif "Jon":
    print("Access granted.")
elif "Inbar":
    print("Access granted.")
else:
    print("Access denied.")

自从 elif "Jon" 将始终为true,因此授予对任何用户的访问权限

解决方案

您可以使用下面的任何一种方法
快速的

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

fcg9iug33#

简单的工程问题,让我们更进一步。

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

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

shyt4zoc

shyt4zoc4#

非空列表、集合、字符串等是可计算的,因此返回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
kmbjn2e3

kmbjn2e35#

接近

数据科学家如何处理这个问题

最简单的方法是不需要比较运算符,而是使用列表。这在安全系统上看起来令人印象深刻,因为您学会了访问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的 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)

这个问题可以从任何Angular 来解决:内存管理、安全性,或者仅仅通过一个有机列表或打包的orm。
希望这有帮助。

相关问题