在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
为什么两种方法的结果不同?相等运算符是否以不同方式处理元组?
答案 0 :(得分:155)
前两个表达式都解析为元组:
(0, 0) == 0
(False
),后跟0
0
,然后是0 == (0, 0)
(仍然是False
。表达式以这种方式拆分,因为逗号分隔符与相等运算符相比具有相对优先级:Python看到包含两个表达式的元组,其中一个恰好是相等测试,而不是两个元组之间的相等测试
但在第三个示例中,a = 0, 0
不能成为元组。元组是值的集合,与等式测试不同,赋值在Python中没有任何价值。赋值不是表达式,而是声明;它没有可以包含在元组或任何其他周围表达式中的值。如果您尝试使用(a = 0), 0
之类的内容以强制解释为元组,则会出现语法错误。这样就可以将一个元组赋值给一个变量 - 通过编写a = (0, 0)
可以使其更明确 - 作为a = 0, 0
的唯一有效解释。
答案 1 :(得分:68)
您在所有3个实例中看到的是该语言的grammar specification的结果,以及解析源代码中遇到的令牌如何生成解析树。
看一下这个低级代码应该可以帮助你理解幕后发生的事情。我们可以使用这些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
。
答案 2 :(得分:20)
解释问题的另一种方法:你可能熟悉字典文字
{ "a": 1, "b": 2, "c": 3 }
和数组文字
[ "a", "b", "c" ]
和元组文字
( 1, 2, 3 )
但你没有意识到的是,与字典和数组文字不同,你通常在元组文字周围看到的括号不是文字语法的一部分。元组的文字语法只是由逗号分隔的表达式序列:
1, 2, 3
(formal grammar for 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
答案 3 :(得分: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
。
答案 4 :(得分:6)
在第一篇文章中,Python正在制作两个元组的元组:
(0, 0) == 0
,其结果为False
0
在第二个方面,反之亦然。
答案 5 :(得分: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)进行比较。
答案 6 :(得分:0)
我有一个类似的问题。我不是计算机科学家,我是软件工程师或计算机程序员。所以我问了python解释器,这是我凭经验发现的。
>>> t1 = ()
>>> "True" if t1 else "False"
'False'
>>> t1 = (False) # That's because t1 is not a tuple!
>>> "True" if t1 else "False"
'False'
>>> t1 = (False,) # t1 is a tuple. So , is an operator as mentioned above
>>> "True" if t1 else "False"
'True'
>>> t1 = (False, 1)
>>> "True" if t1 else "False"
'True'
>>> t1 = (False, False)
>>> "True" if t1 else "False"
'True'
>>> type(False,)
<class 'bool'>
>>> type((False,))
<class 'tuple'>
>>> type(False)
<class 'bool'>
>>> type((False))
<class 'bool'>
>>>
我做了很多测试,我发现的唯一被评估为False的元组是空元组。
我在这项练习中也学到了一些东西。很多新秀都使用这种习语:
if BOOLEAN_EXPRESSION == False:
代替
if not BOOLEAN_EXPRESSION:
“为什么这是一件坏事?”,他们问我。现在,我有了一个很好的答案:
>>> (False,) == False
False
>>> t1=(False,)
>>> "True" if t1 else "False"
'True'
>>> t1 == False
False
>>>
>>> t1=(False,)
>>> "True" if t1 else "False"
'True'
>>> t1 == False
False
>>> t1 is False
False
>>> not t1 is False
True
>>> not ( t1 is False )
True
>>>
>>> "True" if t1 else "False"
'True'
>>> "True" if not t1 else "False"
'False'
>>> "True" if t1 == True else "False"
'False'
>>>
因此,即使(False,)的计算结果为False,也不是 False。
感谢您提出这个问题,以引起我的注意。这是一个很好的问题。