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


118

在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

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

Answers:


156

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

  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


17
我想说的是逗号操作具有较低的比平等优先的,因为平等先于逗号操作符的评价:平等比逗号运算符优先级越高。但这始终是混乱的根源。只是想指出其他消息来源可能会扭转局面。

2
您可以通过说,绑定不如来避免较低/较高的混乱状态,以免混淆==
amalloy '17


48
文档可以声称他们想要的一切,但这并不重要。您可以编写一个解析器,以便每个运算符都能得到自己的结果,并且在实现中的任何地方都没有显式的“优先级”,但这不会阻止这些语法单元成为运算符。您可以以某些实现特定的方式重新定义“ operator” ,这显然是他们在Python中所做的,但这并没有改变该术语的含义。逗号实际上是产生元组的运算符。它的可操作性例如以括号显示其相对优先级的方式。
马克·里德

68

您在所有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


20

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

{ "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

17

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

# 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


6

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

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

在第二个方面则相反。


0

看这个例子:

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)进行比较。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.