Чому в Python "0, 0 == (0, 0)" дорівнює "(0, помилково)"?


118

У 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

Чому результат відрізняється між двома підходами? Чи поводиться оператор рівності по-різному з кортежами?

Відповіді:


156

Перші два вирази обидва розбирають як кортежі:

  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.

Отже, навіть без дужок, які привласнюються a, і це, і bприсвоєне значення (0,0), так a == bце і є True.


17
Я б сказав, що оператор кома має нижчий пріоритет, ніж рівність, оскільки оцінка рівності передує оператору коми: рівність має вищий пріоритет, ніж оператор коми. Але це завжди є джерелом плутанини; просто хотів зазначити, що інші джерела можуть перевернути речі.
tomsmeding

2
Ви можете уникнути нижче / вище за багатослів'я плутанину, а НЕ кажучи , що ,пов'язує менш щільно , ніж ==.
amalloy

4
Кома не є оператором docs.python.org/3.4/faq/…
Chris_Rands

48
Документи можуть стверджувати, що все, що вони хочуть, але це не має значення. Ви можете написати аналізатор, щоб кожен оператор отримував власне виробництво і не було явного «пріоритету» ніде в реалізації, але це не перешкоджає цим синтаксичним одиницям бути операторами. Ви можете переосмислити «оператора» певним чином для конкретного впровадження , що, очевидно, те, що вони зробили в Python, але це не змінює значення цього терміна. Кома фактично є оператором, який виробляє кортежі. Наприклад, його оперативність показує, як дужки впливають на його відносну перевагу.
Марк Рід

68

Те, що ви бачите у всіх 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. Потім з цим результатом будується кортеж і останній 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.


20

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

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

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.


6

У першому Python створює кортеж з двох речей:

  1. Вираз (0, 0) == 0, який оцінює доFalse
  2. Постійна 0

У другому - навпаки.


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) у прикладі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.