In many cases, Python looks and behaves like natural English, but this is one case where that abstraction fails. People can use context clues to determine that "Jon" and "Inbar" are objects joined to the verb "equals", but the Python interpreter is more literal minded.
在许多情况下,Python的外观和行为都像自然的英语,但这是抽象失败的一种情况。人们可以使用上下文线索来确定“Jon”和“Inbar”是连接到动词“equals”的对象,但Python解释器更注重字面意思。
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
在逻辑上等同于:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
对于用户Bob来说,这相当于:
if (False) or ("Jon") or ("Inbar"):
The or
operator chooses the first operand that is "truthy", i.e. which would satisfy an if
condition (or the last one, if none of them are "truthy"):
OR运算符选择第一个“真”的操作数,即满足IF条件的第一个操作数(如果它们都不是“真”的,则选择最后一个):
if "Jon":
Since "Jon" is truthy, the if
block executes. That is what causes "Access granted" to be printed regardless of the name given.
由于“jon”为真,因此执行if块。这就是导致打印“授予访问权限”的原因,无论给定的名称是什么。
All of this reasoning also applies to the expression if "Kevin" or "Jon" or "Inbar" == name
. the first value, "Kevin"
, is true, so the if
block executes.
所有这些推理也适用于表达式if“Kevin”or“Jon”or“Inbar”== name。第一个值“Kevin”为true,因此执行if块。
There are two common ways to properly construct this conditional.
有两种常见的方法可以正确地构造这个条件。
Use multiple ==
operators to explicitly check against each value:
if name == "Kevin" or name == "Jon" or name == "Inbar":
Compose a collection of valid values (a set, a list or a tuple for example), and use the in
operator to test for membership:
if name in {"Kevin", "Jon", "Inbar"}:
In general of the two the second should be preferred as it's easier to read and also faster:
总的来说,第二种应该是首选的,因为它更容易阅读,也更快:
>>> 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
For those who may want proof that if a == b or c or d or e: ...
is indeed parsed like this. The built-in ast
module provides an answer:
对于那些可能想要证明如果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())]))
As one can see, it's the boolean operator or
applied to four sub-expressions: comparison a == b
; and simple expressions c
, d
, and e
.
可以看到,它是布尔运算符或应用于四个子表达式:比较a==b;以及简单的表达式c、d和e。
Summarising all existing answers
(And adding a few of my points)
Explanation :
if name == "Kevin" or "Jon" or "Inbar":
is logically equivalent to:
在逻辑上等同于:
if (name == "Kevin") or ("Jon") or ("Inbar"):
Which, for user Bob, is equivalent to:
对于用户Bob来说,这相当于:
if (False) or ("Jon") or ("Inbar"):
NOTE : Python evaluates the logical value of any non-zero integer as True
. Therefore, all Non-empty lists, sets, strings, etc. are evaluable and return True
注意:Python会将任何非零整数的逻辑值计算为True。因此,所有非空列表、集合、字符串等都是可计算的,并返回True
The or
operator chooses the first argument with a positive truth value.
OR运算符选择具有正真值值的第一个参数。
Therefore, "Jon" has a positive truth value and the if block executes, since it is now equivalent to
因此,“jon”具有正的真值,并且执行if块,因为它现在等同于
if (False) or (True) or (True):
That is what causes "Access granted" to be printed regardless of the name input.
这就是导致无论输入名称如何都会打印“授予访问权限”的原因。
Solutions :
Solution 1 : Use multiple ==
operators to explicitly check against each value
解决方案1:使用多个==运算符显式检查每个值
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Solution 2 : Compose a collection of valid values (a set, a list or a tuple for example), and use the in
operator to test for membership (faster, preferred method)
解决方案2:组成一个有效值的集合(例如,一个集合、一个列表或一个元组),并使用in运算符来测试成员资格(更快、更好的方法)
if name in {"Kevin", "Jon", "Inbar"}:
print("Access granted.")
else:
print("Access denied.")
OR
或
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Solution 3 : Use the basic (and not very efficient) if-elif-else
structure
解决方案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.")
There are 3 condition checks in if name == "Kevin" or "Jon" or "Inbar":
如果名称==“Kevin”、“Jon”或“Inbar”,则有3个条件检查:
- name == "Kevin"
- "Jon"
- "Inbar"
and this if statement is equivalent to
这条if语句等同于
if name == "Kevin":
print("Access granted.")
elif "Jon":
print("Access granted.")
elif "Inbar":
print("Access granted.")
else:
print("Access denied.")
Since elif "Jon"
will always be true so access to any user is granted
由于elif“jon”将始终为真,因此授予对任何用户的访问权限
Solution
You can use any one method below
您可以使用下面的任何一种方法
Fast
快地
if name in ["Kevin", "Jon", "Inbar"]:
print("Access granted.")
else:
print("Access denied.")
Slow
慢的
if name == "Kevin" or name == "Jon" or name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Slow + Unnecessary code
速度慢+不必要的代码
if name == "Kevin":
print("Access granted.")
elif name == "Jon":
print("Access granted.")
elif name == "Inbar":
print("Access granted.")
else:
print("Access denied.")
Not-empty lists, sets, strings, etc. are evaluable and, therefore, return True.
Therefore, when you say:
因此,当你说:
a = "Raul"
if a == "Kevin" or "John" or "Inbar":
pass
You are actually saying:
你实际上是在说:
if "Raul" == "Kevin" or "John" != "" or "Inbar" != "":
pass
Since at least one of "John" and "Inbar" is not an empty string, the whole expression always returns True!
因为“John”和“Inbar”中至少有一个不是空字符串,所以整个表达式总是返回True!
The solution:
a = "Raul"
if a == "Kevin" or a == "John" or a == "Inbar":
pass
or:
或者:
a = "Raul"
if a in {"Kevin", "John", "Inbar"}:
pass
Simple engineering problem, let's simply it a bit further.
简单的工程问题,让我们简单地讲得更深入一点。
In [1]: a,b,c,d=1,2,3,4
In [2]: a==b
Out[2]: False
But, inherited from the language C, Python evaluates the logical value of a non zero integer as True.
但是,继承自C语言的Python将非零整数的逻辑值求值为True。
In [11]: if 3:
...: print ("yey")
...:
yey
Now, Python builds on that logic and let you use logic literals such as or on integers, and so
现在,Python构建在该逻辑之上,允许您对整数使用逻辑文字,如或,等等
In [9]: False or 3
Out[9]: 3
Finally
终于
In [4]: a==b or c or d
Out[4]: 3
The proper way to write it would be:
正确的写法应该是:
In [13]: if a in (b,c,d):
...: print('Access granted')
For safety I'd also suggest you don't hard code passwords.
为了安全起见,我还建议您不要硬编码密码。
Using match
/case
in Python 3.10 and above
Python 3.10 adds a new syntax to the language. It's officially described as "structural pattern matching", but most people call it according to the syntax: "match
/case
".
Python3.10为该语言添加了新的语法。它被官方描述为“结构化模式匹配”,但大多数人是根据语法来称呼它的:“Match/Case”。
We can use this special syntax for an example like in the question, by making one "case" that matches all the accepted usernames, and using the "wildcard" case _
in place of the else
. Thus:
我们可以将这种特殊的语法用于类似问题中的示例,方法是创建一个与所有接受的用户名匹配的“case”,并使用“通配符”case_来代替Else。因此:
name = input("Hello. Please enter your name: ")
match name:
case "Kevin" | "Jon" | "Inbar":
print("Access granted.")
case _:
print("Access denied.")
Note that cases are "combined" using |
, not or
. This is a special syntax: Python does not try to compute "Kevin" | "Jon" | "Inbar"
first (|
doesn't work with strings), but instead interprets the entire line differently because it starts with case
.
请注意,大小写是使用|、Not或“组合”的。这是一种特殊的语法:Python不会首先尝试计算“Kevin”|“Jon”|“Inbar”(|不适用于字符串),而是以不同的方式解释整行,因为它以大小写开头。
Besides some other rather rarer useful cases for the walrus operator already mentioned. This also tend to be a useful case as well.
此外,对于已经提到的海象操作员来说,还有其他一些相当罕见的有用案例。这也往往是一个有用的案例。
def calc_value():
return 43
if (v := calc_value()) == 43 and v > 42:
print('happy short, efficient and readable code')
This works because each part of the if-statement
is read separately. So (v := calc_value())
is executed and a value is assigned to v
and if the first fails, you still have v in the namespace for different conditions or calculations.
这是可行的,因为if语句的每个部分都是单独读取的。因此(v:=calc_Value())被执行,并为v赋值,如果第一个失败,您仍然可以在名称空间中为不同的条件或计算保留v。
Approaches
How a data scientist approaches this problem
The simplest way possible is to eliminate the need for comparison operators and use a list. This looks impressive on security systems because you learn to access ORMs.
最简单的方法是消除对比较运算符的需要,而使用列表。这在安全系统上看起来令人印象深刻,因为您学习了访问ORM。
user = input("Enter name: ")
if user in {"Bob", "Kevin", "Joe"}:
print("Access granted, " + str(user) + ".")
else:
print("Access denied.")
Or, you can resemble the exact same code above, just put the list of registered users in their own list:
或者,您可以类似于上面的完全相同的代码,只需将注册用户列表放在他们自己的列表中:
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.")
If you wanted to complete this protocol safely without the risk of attack, set up double parameters. This would check your mini-ORM for first
and last
name fields, as well as a password
or secret question
key. Objects can be sorted like this if you want to efficiently lazy-load user credentials without hashing:
如果您想要安全地完成此协议而没有攻击风险,请设置双参数。这将检查您的mini-ORM中的名字和姓氏字段,以及密码或机密问题密钥。如果您希望在不进行散列的情况下高效地延迟加载用户凭据,则可以按如下方式对对象进行排序:
def lazy(i):
j = 0 # For example
while j < i:
yield j
j += 1
The loop will consume only the yielded values to save time and energy on your system:
循环将只消耗产生的值,以节省系统的时间和能量:
You can then do something with the iterated list:
然后,您可以对迭代列表执行某些操作:
for j in lazy_range(10):
do_something_here(j)
This problem can be approached from any angle: memory management, security, or simply by an organic list or packaged ORM.
这个问题可以从任何角度来解决:内存管理、安全性,或者简单地通过有机列表或打包的ORM。
更多回答
Is there a specific reason to choose a tuple ("Kevin", "Jon", "Inbar")
instead of a set {"Kevin", "Jon", "Inbar"}
?
选择元组(“Kevin”、“Jon”、“Inbar”)而不是集合(“Kevin”、“Jon”、“Inbar”)是否有特定的原因?
Not really, since both work if the values are all hashable. Set membership testing has better big-O complexity than tuple membership testing, but constructing a set is a little more expensive than constructing a tuple. I think it's largely a wash for small collections like these. Playing around with timeit, a in {b, c, d}
is about twice as fast as a in (b, c, d)
on my machine. Something to think about if this is a performance-critical piece of code.
并非如此,因为如果值都是Hasable,则两者都可以工作。集合成员关系测试比元组成员关系测试具有更好的BIG-O复杂性,但是构造一个集合比构造一个元组稍微昂贵一些。我认为,对于像这样的小系列来说,这在很大程度上是一种洗涤。玩弄时间,在我的机器上,a in{b,c,d}的速度大约是a in(b,c,d)的两倍。如果这是一段对性能至关重要的代码,需要考虑一下。
In modern Python, it recognizes that the set is a constant and makes it a frozenset
instead, so the constructing set overhead is not there. dis.dis(compile("1 in {1, 2, 3}", '<stdin>', 'eval'))
在现代的Python中,它将集合识别为常量,并将其改为冻结集合,因此不存在构造集合的开销。Dis.dis(编译(“1 in{1,2,3}”,‘’,‘val’))
FWIW I think you should re-add the tuple as that is simpler for folks to understand than a set.
FWIW我认为您应该重新添加元组,因为这比集合更容易理解。
good otherwise but "You are actually saying:" is wrong, that's not how or
works. The value of the expression is "John"
, not True
.
否则很好,但“你实际上是在说:”是错误的,这不是或如何工作的。该表达式的值是“John”,而不是True。
我是一名优秀的程序员,十分优秀!