为什么在 Python 中“0, 0 == (0, 0)”等于“(0, False)”?
Why in Python does "0, 0 == (0, 0)" equal "(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
为什么两种方法的结果不同?相等运算符是否以不同方式处理元组?
前两个表达式都解析为元组:
(0, 0) == 0
(也就是False
),然后是0
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
.
在第一个中 Python 正在制作一个包含两件事的元组:
- 表达式
(0, 0) == 0
,计算结果为 False
- 常量
0
第二个是相反的。
您在所有 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)
.
案例二: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
。
在执行操作的顺序周围添加几个括号可能有助于您更好地理解结果:
# 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
.
组成的两个表达式
另一种解释问题的方法:您可能熟悉字典文字
{ "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
看这个例子:
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)。
我也有类似的问题。我不是计算机科学家,我要么是软件工程师,要么是计算机程序员。所以我问了 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。
感谢您提请我注意这个问题。这是一个很好的问题。
在 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, 0) == 0
(也就是False
),然后是0
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
.
在第一个中 Python 正在制作一个包含两件事的元组:
- 表达式
(0, 0) == 0
,计算结果为False
- 常量
0
第二个是相反的。
您在所有 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)
.
案例二: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
。
在执行操作的顺序周围添加几个括号可能有助于您更好地理解结果:
# 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
.
另一种解释问题的方法:您可能熟悉字典文字
{ "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
看这个例子:
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)。
我也有类似的问题。我不是计算机科学家,我要么是软件工程师,要么是计算机程序员。所以我问了 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。
感谢您提请我注意这个问题。这是一个很好的问题。