为什么“a == x or y or z"总是评估为 True? [英] Why does "a == x or y or z" always evaluate to True?

查看:30
本文介绍了为什么“a == x or y or z"总是评估为 True?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

name = input(你好,请输入你的名字:")if name == "Kevin";或乔恩"或内巴":打印(授予访问权限.")别的:打印(拒绝访问.")

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

你好.请输入您的姓名:鲍勃授予访问权限.

为什么会出现这种情况?我已经明确声明仅在 name 等于 Kevin、Jon 或 Inbar 时才授予访问权限.我也尝试过相反的逻辑,if Kevin"或乔恩"或Inbar"==名称,但结果是一样的.

<块引用>

注意:这个问题旨在作为这个非常常见问题的规范重复目标.还有另一个流行的问题 如何针对单个值测试多个变量? 具有相同的基本问题,但比较目标是相反的.这个问题不应该作为那个问题的重复来结束,因为这个问题是 Python 新手遇到的,他们可能难以将相反问题的知识应用到他们的问题中.

解决方案

在许多情况下,Python 的外观和行为都像自然英语,但这是抽象失败的一种情况.人们可以使用上下文线索来确定Jon"和Inbar"是连接到动词等于"的对象,但 Python 解释器更注重字面意思.

if name == Kevin";或乔恩"或内巴":

在逻辑上等同于:

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

对于用户 Bob 来说,相当于:

if (False) or ("Jon") or ("Inbar"):

or 运算符选择具有正 真值:

如果乔恩":

自从乔恩"有一个正真值,if 块就会执行.这就是导致允许访问"的原因.无论给出的名称如何,都要打印.

所有这些推理也适用于表达式 if Kevin";或乔恩"或Inbar"== 名称.第一个值 Kevin" 为真,因此 if 块执行.


有两种常见的方法可以正确构造此条件.

  1. 使用多个 == 运算符来明确检查每个值:

    if name == Kevin";或姓名==乔恩"或名称==Inbar":

  2. 组成一组有效值(例如一个集合、一个列表或一个元组),并使用 in 运算符来测试成员资格:

    if name in {Kevin", Jon", Inbar"}:

总的来说,在两者中应该首选第二个,因为它更容易阅读且速度更快:

<预><代码>>>>导入时间>>>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 模块提供了答案:

<预><代码>>>>进口AST>>>ast.parse("a == b or c or d or e", "", "eval")<ast.Expression 对象在 0x7f929c898220>>>>打印(ast.dump(_,缩进= 4))表达(身体=BoolOp(op=Or(),值=[相比(left=Name(id='a', ctx=Load()),操作=[等式()],比较器=[名称(id='b', ctx=Load())]),名称(id='c', ctx=Load()),名称(id='d', ctx=Load()),名称(id='e', ctx=Load())]))

如我们所见,它是应用于四个子表达式的布尔运算符or:comparison a == b;和简单的表达式 cde.

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.

Note: this question is intended as the canonical duplicate target of this very common problem. There is another popular question How to test multiple variables 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.

解决方案

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.

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:

if (False) or ("Jon") or ("Inbar"):

The or operator chooses the first argument with a positive truth value:

if "Jon":

And since "Jon" has a positive truth value, the if block executes. That is what causes "Access granted" to be printed regardless of the name given.

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.


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:

>>> 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 == x or y or z"总是评估为 True?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆