为什么`a == b或c或d`总是评估为True?

时间:2013-11-15 13:45:35

标签: python boolean boolean-expression

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

import sys

print("Hello. Please enter your name:")
name = sys.stdin.readline().strip()
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,但结果是一样的。

5 个答案:

答案 0 :(得分:128)

在许多情况下,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运算符选择带有正truth value的第一个参数:

if ("Jon"):

由于“Jon”具有正的真值,因此执行if块。这就是导致“授予访问权限”被打印的原因,无论给出的名称如何。

所有这些推理也适用于表达式if "Kevin" or "Jon" or "Inbar" == name。第一个值"Kevin"为true,因此if块执行。


正确构建此条件有两种常用方法。

  1. 使用多个==运算符明确检查每个值:
    if name == "Kevin" or name == "Jon" or name == "Inbar":

  2. 撰写一系列有效值,并使用in运算符测试成员资格:
    if name in ("Kevin", "Jon", "Inbar"):

  3. 一般来说,第二个应该是首选,因为它更容易阅读,也更快:

    In [1]: name = "Inbar"
    
    In [2]: %timeit name == "Kevin" or name == "Jon" or name == "Inbar"
    10000000 loops, best of 3: 116 ns per loop
    
    In [3]: %timeit name in ("Kevin", "Jon", "Inbar")
    10000000 loops, best of 3: 65.2 ns per loop
    

答案 1 :(得分:2)

if name == "Kevin" or "Jon" or "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"始终为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.")

答案 2 :(得分:1)

非空列表,集合,字符串等是可评估的,因此返回True。

因此,当您说:

library(tidyverse)
cleaned_moneygram <- moneygram_prices %>% 
  as_tibble() %>% 
  rename(transfer_fee = 9) %>% 
  rename(destination_country = 2) %>% 
  separate(time, into = c("year","time"),sep = " ") %>% 
  mutate(year = substr(year,1,4)) %>% 
  mutate(year = as.numeric(year)) %>% 
  mutate(firm = "MoneyGram") %>% 
  select(c("send_country","destination_country","payment_method","year","exchange_rate","transfer_fee","firm")) %>% 
  separate(transfer_fee, into = c("value","currency"),sep = " ") %>% 
  mutate(tfer_fee_usd = case_when(
    currency == "USD" ~ value * 1.0,
    currency == "EUR" ~ value * 1.2,
    currency == "NOK" ~ value * 1170.5,
    currency == "UOF" ~ value * 123.2
  ))
#> Error in as_tibble(.): object 'moneygram_prices' not found

您实际上是在说:

a = "Raul"
if a == "Kevin" or "John" or "Inbar":
    pass

由于“ John”和“ Inbar”中的至少一个不是空字符串,因此整个表达式始终返回True!

解决方案:

if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
    pass

或:

a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
    pass

答案 3 :(得分:0)

简单的工程问题,让我们再简单一点。

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

为了安全起见,我还建议您不要对密码进行硬编码。

答案 4 :(得分:0)

方法

数据科学家如何解决这个问题

最简单的方法是消除对比较运算符的需求,并使用列表。在安全系统上,这看起来令人印象深刻,因为您学会了访问ORM。

user = input("Enter name: ")

if user in {"Bob", "Kevin", "Joe"}:
   print("Access granted, " + str(user) + ".")
else:
   print("Access denied.")

或者,您可以类似于上面的 exact 相同代码,只需将注册用户列表放在自己的列表中即可:

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中的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)

可以从任何角度解决此问题:内存管理,安全性,或者仅通过有机列表或打包的ORM来解决。

希望这会有所帮助。