2016-11-28 8 views
3

Не могли бы вы объяснить, как выглядит синтаксическое дерево разбора для прикованного сравнения?Как три сравнения операндов работают в Python под капотом?

Насколько я понимаю на большинстве языков он строит узлы на основе ассоциативности операторов, поэтому в a < b < c будет логическое значение либо как левый, либо правый операнд.

Но в Python такое выражение почти эквивалентно a < b and b < c (с оценкой b).

Что такое порождающее правило грамматики для такого преобразования? В основном, что делает интерпретатор Python для построения дерева разбора в таком случае?

+0

правило Грамматика просто 'сравнения: выражение (comp_op выражение) *'. –

+0

"такое выражение почти эквивалентно' a

+0

@ PM2Ring да, вот почему я написал «почти», они эквивалентны, если в вычислениях нет побочных эффектов. –

ответ

6

comparison grammar не то, что здесь интересно, это просто позволяет добавлять несколько компараторов к оператору:

comparison ::= or_expr (comp_operator or_expr)* 
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!=" 
        | "is" ["not"] | ["not"] "in" 

Так позволяет задать Python анализатор непосредственно, с помощью ast module (который просто просит сам Python компилятор чтобы вернуть только абстрактное синтаксическое дерево):

>>> import ast 
>>> ast.dump(ast.parse('a > b > c', mode='eval')) 
"Expression(body=Compare(left=Name(id='a', ctx=Load()), ops=[Gt(), Gt()], comparators=[Name(id='b', ctx=Load()), Name(id='c', ctx=Load())]))" 

Так есть только одногоCompare узел, с несколькими операторами и компаратор с:

Compare(
    left=Name(id='a'), 
    ops=[Gt(), Gt()], 
    comparators=[Name(id='b'), Name(id='c')]) 

(я опустил Expression и ctx частей).

Это позволяет интерпретатору оценивать компараторы по мере необходимости (например, если a < b является ложным, остальные компараторы не обязательно должны учитываться).

Полученный байт-код использует условные переходы, чтобы пропустить оставшиеся сравнения:

>>> import dis 
>>> dis.dis(compile('a > b > c', '', 'eval')) 
    1   0 LOAD_NAME    0 (a) 
       2 LOAD_NAME    1 (b) 
       4 DUP_TOP 
       6 ROT_THREE 
       8 COMPARE_OP    4 (>) 
      10 JUMP_IF_FALSE_OR_POP 18 
      12 LOAD_NAME    2 (c) 
      14 COMPARE_OP    4 (>) 
      16 RETURN_VALUE 
     >> 18 ROT_TWO 
      20 POP_TOP 
      22 RETURN_VALUE