标签归档:operator-precedence

为什么在Python中“ 0,0 ==(0,0)”等于“(0,False)”?

问题:为什么在Python中“ 0,0 ==(0,0)”等于“(0,False)”?

在Python中(我仅使用Python 3.6进行过检查,但我相信它也适用于许多以前的版本):

(0, 0) == 0, 0   # results in a two element tuple: (False, 0)
0, 0 == (0, 0)   # results in a two element tuple: (0, False)
(0, 0) == (0, 0) # results in a boolean True

但:

a = 0, 0
b = (0, 0)
a == b # results in a boolean True

为什么两种方法的结果不同?相等运算符是否以不同的方式处理元组?

In Python (I checked only with Python 3.6 but I believe it should hold for many of the previous versions as well):

(0, 0) == 0, 0   # results in a two element tuple: (False, 0)
0, 0 == (0, 0)   # results in a two element tuple: (0, False)
(0, 0) == (0, 0) # results in a boolean True

But:

a = 0, 0
b = (0, 0)
a == b # results in a boolean True

Why does the result differ between the two approaches? Does the equality operator handle tuples differently?


回答 0

前两个表达式都解析为元组:

  1. (0, 0) == 0(即False),然后是0
  2. 0,其次是0 == (0, 0)(仍然False如此)。

由于逗号分隔符相对于相等运算符具有较高的优先级,因此将表达式进行拆分:Python看到一个元组包含两个表达式,其中一个恰好是一个相等测试,而不是两个元组之间的相等测试。

但是在第二组语句中,a = 0, 0 不能是元组。元组是值的集合,与相等性测试不同,赋值在Python中没有值。赋值不是表达式,而是语句。它没有可包含在元组或任何其他周围表达式中的值。如果您尝试执行类似(a = 0), 0的操作以强制将其解释为元组,则会出现语法错误。这样,将元组分配给变量(可以通过编写使其更明确)a = (0, 0)作为对的唯一有效解释a = 0, 0

因此,即使没有对的括号a,它和都b获得了赋值(0,0),因此a == b也是如此True

The first two expressions both parse as tuples:

  1. (0, 0) == 0 (which is False), followed by 0
  2. 0, followed by 0 == (0, 0) (which is still False that way around).

The expressions are split that way because of the relative precedence of the comma separator compared to the equality operator: Python sees a tuple containing two expressions, one of which happens to be an equality test, instead of an equality test between two tuples.

But in your second set of statements, a = 0, 0 cannot be a tuple. A tuple is a collection of values, and unlike an equality test, assignment has no value in Python. An assignment is not an expression, but a statement; it does not have a value that can be included into a tuple or any other surrounding expression. If you tried something like (a = 0), 0 in order to force interpretation as a tuple, you would get a syntax error. That leaves the assignment of a tuple to a variable – which could be made more explicit by writing it a = (0, 0) – as the only valid interpretation of a = 0, 0.

So even without the parentheses on the assignment to a, both it and b get assigned the value (0,0), so a == b is therefore True.


回答 1

您在所有3种情况下看到的都是语言语法规范的结果,以及如何解析源代码中遇到的标记以生成解析树。

看看这个低级代码应该可以帮助您了解幕后情况。我们可以使用以下python语句,将它们转换为字节码,然后使用以下dis模块对其进行反编译:

情况1: (0, 0) == 0, 0

>>> dis.dis(compile("(0, 0) == 0, 0", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               0 (0)
              6 COMPARE_OP               2 (==)
              9 LOAD_CONST               0 (0)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

(0, 0)首先与0第一个进行比较,然后对进行评估False。然后,使用此结果构造一个元组,最后0得到结果(False, 0)

情况2: 0, 0 == (0, 0)

>>> dis.dis(compile("0, 0 == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               0 (0)
              6 LOAD_CONST               2 ((0, 0))
              9 COMPARE_OP               2 (==)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

将元组构造0为第一个元素。对于第二个元素,执行与第一种情况相同的检查,并求值为False,因此得到(0, False)

情况3: (0, 0) == (0, 0)

>>> dis.dis(compile("(0, 0) == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               3 ((0, 0))
              6 COMPARE_OP               2 (==)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE

如您所见,在这里,您只是比较这两个(0, 0)元组并返回True

What you see in all 3 instances is a consequence of the grammar specification of the language, and how tokens encountered in the source code are parsed to generate the parse tree.

Taking a look at this low level code should help you understand what happens under the hood. We can take these python statements, convert them into byte code and then decompile them using the dis module:

Case 1: (0, 0) == 0, 0

>>> dis.dis(compile("(0, 0) == 0, 0", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               0 (0)
              6 COMPARE_OP               2 (==)
              9 LOAD_CONST               0 (0)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

(0, 0) is first compared to 0 first and evaluated to False. A tuple is then constructed with this result and last 0, so you get (False, 0).

Case 2: 0, 0 == (0, 0)

>>> dis.dis(compile("0, 0 == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               0 (0)
              6 LOAD_CONST               2 ((0, 0))
              9 COMPARE_OP               2 (==)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

A tuple is constructed with 0 as the first element. For the second element, the same check is done as in the first case and evaluated to False, so you get (0, False).

Case 3: (0, 0) == (0, 0)

>>> dis.dis(compile("(0, 0) == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               3 ((0, 0))
              6 COMPARE_OP               2 (==)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE

Here, as you see, you’re just comparing those two (0, 0) tuples and returning True.


回答 2

解释问题的另一种方法:您可能熟悉字典文字

{ "a": 1, "b": 2, "c": 3 }

和数组文字

[ "a", "b", "c" ]

和元组文字

( 1, 2, 3 )

但是您没有意识到的是,与字典和数组文字不同,您通常在元组文字周围看到的括号不是文字语法的一部分。元组的文字语法只是一系列用逗号分隔的表达式:

1, 2, 3

Python形式语法语言中的“ exprlist” )。

现在,您期望数组文字是什么

[ 0, 0 == (0, 0) ]

评价?看起来可能更像是应该

[ 0, (0 == (0, 0)) ]

当然是哪个[0, False]。类似地,使用显式带括号的元组文字

( 0, 0 == (0, 0) )

得到并不奇怪(0, False)。但是括号是可选的。

0, 0 == (0, 0)

是同一回事。这就是为什么你得到(0, False)


如果您想知道为什么元组文字周围的括号是可选的,那主要是因为必须以这种方式编写解构分配会很烦人:

(a, b) = (c, d) # meh
a, b = c, d     # better

Another way to explain the problem: You’re probably familiar with dictionary literals

{ "a": 1, "b": 2, "c": 3 }

and array literals

[ "a", "b", "c" ]

and tuple literals

( 1, 2, 3 )

but what you don’t realize is that, unlike dictionary and array literals, the parentheses you usually see around a tuple literal are not part of the literal syntax. The literal syntax for tuples is just a sequence of expressions separated by commas:

1, 2, 3

(an “exprlist” in the language of the formal grammar for Python).

Now, what do you expect the array literal

[ 0, 0 == (0, 0) ]

to evaluate to? That probably looks a lot more like it should be the same as

[ 0, (0 == (0, 0)) ]

which of course evaluates to [0, False]. Similarly, with an explicitly parenthesized tuple literal

( 0, 0 == (0, 0) )

it’s not surprising to get (0, False). But the parentheses are optional;

0, 0 == (0, 0)

is the same thing. And that’s why you get (0, False).


If you’re wondering why the parentheses around a tuple literal are optional, it is largely because it would be annoying to have to write destructuring assignments that way:

(a, b) = (c, d) # meh
a, b = c, d     # better

回答 3

在执行操作的顺序周围加上几个括号可以帮助您更好地理解结果:

# Build two element tuple comprising of 
# (0, 0) == 0 result and 0
>>> ((0, 0) == 0), 0
(False, 0)

# Build two element tuple comprising of
# 0 and result of (0, 0) == 0 
>>> 0, (0 == (0, 0))
(0, False)

# Create two tuples with elements (0, 0) 
# and compare them
>>> (0, 0) == (0, 0) 
True

逗号用于分隔表达式(使用括号,我们可以强制使用不同的行为)。查看您列出的代码段时,逗号,会将其分开并定义将对哪些表达式求值:

(0, 0) == 0 ,   0
#-----------|------
  expr 1      expr2

元组(0, 0)也可以类似的方式分解。逗号分隔两个包含文字的表达式0

Adding a couple of parentheses around the order in which actions are performed might help you understand the results better:

# Build two element tuple comprising of 
# (0, 0) == 0 result and 0
>>> ((0, 0) == 0), 0
(False, 0)

# Build two element tuple comprising of
# 0 and result of (0, 0) == 0 
>>> 0, (0 == (0, 0))
(0, False)

# Create two tuples with elements (0, 0) 
# and compare them
>>> (0, 0) == (0, 0) 
True

The comma is used to to separate expressions (using parentheses we can force different behavior, of course). When viewing the snippets you listed, the comma , will separate it and define what expressions will get evaluated:

(0, 0) == 0 ,   0
#-----------|------
  expr 1      expr2

The tuple (0, 0) can also be broken down in a similar way. The comma separates two expressions comprising of the literals 0.


回答 4

在第一个示例中,Python将两件事变成元组:

  1. 表达式(0, 0) == 0,其计算结果为False
  2. 常数 0

在第二个方面则相反。

In the first one Python is making a tuple of two things:

  1. The expression (0, 0) == 0, which evaluates to False
  2. The constant 0

In the second one it’s the other way around.


回答 5

看这个例子:

r = [1,0,1,0,1,1,0,0,0,1]
print(r==0,0,r,1,0)
print(r==r,0,1,0,1,0)

然后结果:

False 0 [1, 0, 1, 0, 1, 1, 0, 0, 0, 1] 1 0
True 0 1 0 1 0

然后比较就和示例中的第一个数字(0和r)进行比较。

look at this example:

r = [1,0,1,0,1,1,0,0,0,1]
print(r==0,0,r,1,0)
print(r==r,0,1,0,1,0)

then result:

False 0 [1, 0, 1, 0, 1, 1, 0, 0, 0, 1] 1 0
True 0 1 0 1 0

then comparison just does to the first number(0 and r) in the example.


为什么([1,0]中的1 = = True)评估为False?

问题:为什么([1,0]中的1 = = True)评估为False?

在查看该问题的答案时,我发现自己不明白自己的答案。

我真的不明白这是如何解析的。为什么第二个示例返回False?

>>> 1 in [1,0]             # This is expected
True
>>> 1 in [1,0] == True     # This is strange
False
>>> (1 in [1,0]) == True   # This is what I wanted it to be
True
>>> 1 in ([1,0] == True)   # But it's not just a precedence issue!
                           # It did not raise an exception on the second example.

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    1 in ([1,0] == True)
TypeError: argument of type 'bool' is not iterable

谢谢你的帮助。我想我一定会错过一些非常明显的东西。


我认为这与链接的重复项有细微的不同:

为什么表达式0 <0 == 0在Python中返回False?

这两个问题都与人类对表达的理解有关。在我看来,似乎有两种评估表达方式的方法。当然,两者都不正确,但是在我的示例中,最后的解释是不可能的。

看着0 < 0 == 0您可以想象每个被评估的部分都可以表达为有意义:

>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True

因此,链接回答了为什么这样评估False

>>> 0 < 0 == 0
False

但是,对于我的示例1 in ([1,0] == True),将其作为一个表达式没有任何意义,因此,除了存在两种(公认错误的)可能的解释之外,似乎只有一种可能:

>>> (1 in [1,0]) == True

When I was looking at answers to this question, I found I didn’t understand my own answer.

I don’t really understand how this is being parsed. Why does the second example return False?

>>> 1 in [1,0]             # This is expected
True
>>> 1 in [1,0] == True     # This is strange
False
>>> (1 in [1,0]) == True   # This is what I wanted it to be
True
>>> 1 in ([1,0] == True)   # But it's not just a precedence issue!
                           # It did not raise an exception on the second example.

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    1 in ([1,0] == True)
TypeError: argument of type 'bool' is not iterable

Thanks for any help. I think I must be missing something really obvious.


I think this is subtly different to the linked duplicate:

Why does the expression 0 < 0 == 0 return False in Python?.

Both questions are to do with human comprehension of the expression. There seemed to be two ways (to my mind) of evaluating the expression. Of course neither were correct, but in my example, the last interpretation is impossible.

Looking at 0 < 0 == 0 you could imagine each half being evaluated and making sense as an expression:

>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True

So the link answers why this evaluates False:

>>> 0 < 0 == 0
False

But with my example 1 in ([1,0] == True) doesn’t make sense as an expression, so instead of there being two (admittedly wrong) possible interpretations, only one seems possible:

>>> (1 in [1,0]) == True

回答 0

Python实际上在这里应用比较运算符链接。表达式被翻译成

(1 in [1, 0]) and ([1, 0] == True)

这显然是False

类似的表达式也会发生这种情况

a < b < c

转换为

(a < b) and (b < c)

(无需b两次评估)。

有关更多详细信息,请参见Python语言文档

Python actually applies comparison operator chaining here. The expression is translated to

(1 in [1, 0]) and ([1, 0] == True)

which is obviously False.

This also happens for expressions like

a < b < c

which translate to

(a < b) and (b < c)

(without evaluating b twice).

See the Python language documentation for further details.