Почему в 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
почему результат отличается между двумя подходами? Оператор равенства обрабатывает Кортежи по-разному?
6 ответов:
первые два выражения, как разобрать, как кортежи:
(0, 0) == 0
(т. е.False
), далее0
0
, следовал по0 == (0, 0)
(который до сих порFalse
что наоборот).выражения разделяются таким образом из-за относительного приоритета разделителя запятой по сравнению с оператором равенства: Python видит кортеж, содержащий два выражения, одно из которых является тестом равенства, а не тест на равенство между двумя кортежами.
но в вашем третьем примере,
a = 0, 0
не может кортежа. Кортеж-это набор значений, и в отличие от теста равенства, присваивание не имеет значения в Python. Присваивание - это не выражение, а оператор; оно не имеет значения, которое может быть включено в кортеж или любое другое окружающее выражение. Если вы пробовали что-то вроде(a = 0), 0
чтобы заставить интерпретацию как кортеж, вы получите синтаксическую ошибку. Это оставляет назначение кортежа переменной – что можно было бы сделать более явным, написав егоa = (0, 0)
- как единственно верное толкованиеa = 0, 0
.
что вы видите во всех 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
. Затем кортеж строится с этим результатом и last0
, так что вы получите(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
.
другой способ объяснить проблему: вы, вероятно, знакомы со словарными литералами
{ "a": 1, "b": 2, "c": 3 }
и литералы массива
[ "a", "b", "c" ]
и кортеж литералы
( 1, 2, 3 )
но вы не понимаете, что, в отличие от словаря и литералов массива, круглые скобки, которые вы обычно видите вокруг литерала кортежа,не является частью буквального синтаксиса. Литеральный синтаксис для кортежей - это просто последовательность выражений, разделенных запятые:
1, 2, 3
("exprlist" на языке формальная грамматика для Python).
теперь, что вы ожидаете массив литерал
[ 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
добавление пары скобок вокруг порядка, в котором выполняются действия, может помочь вам лучше понять результаты:
# 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
.