gpt4 book ai didi

Why does "a == x or y or z" always evaluate to True? How can I compare "a" to all of those?(为什么“a==x或y或z”的计算结果总是为True?我怎么能把“a”和所有这些做比较呢?)

转载 作者:bug小助手 更新时间:2023-10-24 21:55:55 28 4
gpt4 key购买 nike



I am writing a security system that denies access to unauthorized users.

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


name = input("Hello. Please enter your name: ")
if name == "Kevin" or "Jon" or "Inbar":
print("Access granted.")
else:
print("Access denied.")

It grants access to authorized users as expected, but it also lets in unauthorized users!

它像预期的那样向授权用户授予访问权限,但也允许未经授权的用户进入!


Hello. Please enter your name: Bob
Access granted.

Why does this occur? I've plainly stated to only grant access when name equals Kevin, Jon, or Inbar. I have also tried the opposite logic, if "Kevin" or "Jon" or "Inbar" == name, but the result is the same.

为什么会发生这种情况?我已经明确表示,仅当名称等于Kevin、Jon或Inbar时才授予访问权限。我也尝试了相反的逻辑,如果“Kevin”或“Jon”或“Inbar”==name,但结果是相同的。




This question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables for equality against a single value? that has the same fundamental problem, but the comparison targets are reversed. This question should not be closed as a duplicate of that one as this problem is encountered by newcomers to Python who might have difficulties applying the knowledge from the reversed question to their problem.

这个问题的目的是作为这个非常常见的问题的典型重复目标。还有另一个流行的问题,如何测试多个变量与单个值的等价性?这有相同的根本问题,但比较目标是相反的。这个问题不应该作为那个问题的重复而结束,因为这个问题是Python的新手遇到的,他们可能难以将反向问题中的知识应用到他们的问题中。


For in instead of ==, there are solutions here: How to test the membership of multiple values in a list

对于in而不是==,这里有解决方案:如何测试列表中多个值的成员资格


更多回答

Variations of this problem include x or y in z, x and y in z, x != y and z and a few others. While not exactly identical to this question, the root cause is the same for all of them. Just wanted to point that out in case anyone got their question closed as duplicate of this and wasn't sure how it's relevant to them.

这个问题的变体包括z中的x或y,z中的x和y,x!=y和z,以及其他一些问题。虽然与这个问题并不完全相同,但所有这些问题的根本原因都是相同的。只是想指出这一点,以防任何人把他们的问题作为这个问题的副本而结束,并且不确定它与他们有什么关系。

优秀答案推荐

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.

有两种常见的方法可以正确地构造这个条件。



  1. Use multiple == operators to explicitly check against each value:


    if name == "Kevin" or name == "Jon" or name == "Inbar":


  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:


    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)的两倍。如果这是一段对性能至关重要的代码,需要考虑一下。

Tuple or list when using 'in' in an 'if' clause? recommends set literals for membership testing. I'll update my post.

在‘if’子句中使用‘in’时是元组还是列表?建议为成员资格测试设置文本。我会更新我的帖子。

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。

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com