Подтвердить что ты не робот

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

Почему результат отличается от двух подходов? Оператор равенства обрабатывает кортежи по-разному?

4b9b3361

Ответ 1

Первые два выражения анализируются как кортежи:

  • (0, 0) == 0 (который есть False), за которым следует 0
  • 0, а затем 0 == (0, 0) (который по-прежнему False).

Выражения разделены таким образом из-за относительного приоритета разделителя запятой по сравнению с оператором равенства: Python видит кортеж, содержащий два выражения, один из которых является тестом равенства, вместо теста равенства между двумя кортежами.

Но в вашем третьем примере a = 0, 0 не может быть кортежем. Кортеж представляет собой набор значений, и в отличие от теста равенства присваивание не имеет значения в Python. Назначение не является выражением, а выражением; он не имеет значения, которое может быть включено в кортеж или любое другое окружающее выражение. Если вы попытались что-то вроде (a = 0), 0, чтобы заставить интерпретацию как кортеж, вы получите синтаксическую ошибку. Это оставляет назначение кортежа переменной - что может быть сделано более явным, написав его a = (0, 0) - как единственную допустимую интерпретацию a = 0, 0.

Ответ 2

То, что вы видите во всех трех случаях, является следствием грамматической спецификации языка и того, как токены, встречающиеся в исходном коде, анализируются для создания дерева разбора.

Взглянув на этот код низкого уровня, вы сможете понять, что происходит под капотом. Мы можем взять эти операторы 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.

Ответ 3

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

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

Ответ 4

Добавление нескольких круглых скобок вокруг порядка выполнения действий может помочь вам лучше понять результаты:

# 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.

Ответ 5

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

  • Выражение (0, 0) == 0, которое оценивается как False
  • Постоянная 0

Во втором - наоборот.

Ответ 6

посмотрите на этот пример:

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) в примере.