Почему в 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 109

6 ответов:

первые два выражения, как разобрать, как кортежи:

  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.

что вы видите во всех 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. Затем кортеж строится с этим результатом и last 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.

другой способ объяснить проблему: вы, вероятно, знакомы со словарными литералами

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

В первом Python делает кортеж из двух вещей:

  1. выражение (0, 0) == 0, Что означает False
  2. постоянной 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) в Примере.