Лексический анализ Python-логические операторы line & compound

извиняюсь заранее, если это глупый вопрос, но я не могу понять следующее

так я понимаю, что:

конец логической строки представлен маркером NEWLINE

это означает, что грамматика Python определена единственным способом закончить логическую строку с n маркер.

то же самое касается физических линий (скорее EOL, который является EOL платформы, которую вы используется при записи файла, но тем не менее преобразуется в универсальный n Python.

логическая строка может или не может быть эквивалентна одной или нескольким физическим строкам, но обычно это одна, и в большинстве случаев это одна, если вы пишете чистый код.

в том смысле, что:

foo = 'some_value'  # 1 logical line = 1 physical  
foo, bar, baz = 'their', 'corresponding', 'values'  # 1 logical line = 1 physical
some_var, another_var = 10, 10; print(some_var, another_var); some_fn_call()

# the above is still still 1 logical line = 1 physical line
# because ; is not a terminator per se but a delimiter
# since Python doesn't use EBNF exactly but rather a modified form of BNF

# p.s one should never write code as the last line, it's just for educational purposes

не показывая примеры того, как 1 логический эквивалентен > 1 физическому,мой вопрос заключается в следующей части из docs:

операторы не могут пересекать границы логической строки, кроме тех случаев, когда новая строка допускается синтаксисом (например, между высказываниями в составе заявления)

но что это вообще значит? Я понимаю список составных утверждений, будь то:Если, а,, etc. все они состоят из одного или нескольких п. и друг п., в свою очередь, состоит из заголовок и люкс. Этот люкс состоит из одного или нескольких заявления давайте возьмем пример, чтобы быть более конкретным:

так если заявление что-то вроде этого по грамматике (исключая предложения elifs и else):

if_stmt ::=  "if" expression ":" suite

где люкс и его последующие заявления:

suite         ::=  stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement     ::=  stmt_list NEWLINE | compound_stmt
stmt_list     ::=  simple_stmt (";" simple_stmt)* [";"]

таким образом, это означает, что если вы хотите, вы можете выбрать (заданный "|") ваш люкс, чтобы быть 1 из 2 пути:

  1. на той же строке:

    недостатки: не pythonic, и вы не можете иметь другой составной оператор, который вводит новый блок (например, func def, другой if и т. д.)

    advatanges: один лайнер, я думаю

пример:

if 'truthy_string': foo, bar, baz = 1, 2, 3; print('whatever'); call_some_fn();
  1. ввести новый блок:

    преимущества: все, и правильный способ сделать это

пример:

if 'truthy_value':
    first_stmt = 5
    second_stmt = 10
    a, b, c = 1, 2, 3
    func_call()
    result = inception(nested(calls(one_param), another_param), yet_another))

но я не понимаю, как

операторы не могут пересекать границы логической строки, кроме тех случаев, когда новая строка допускается синтаксисом

то, что я вижу выше-это люкс, который является блок кода контролируется если пункт, и, в свою очередь, что люкс, состоит из логических, независимых строк (операторов), где каждый логический линия - это одна физическая линия (по совпадению). Я не вижу, как одна логическая линия может пересекать границы (что в основном просто причудливое слово для конца, предела, который является новой линией), я не вижу, как одно утверждение может пересекать эти границы и охватывать следующее утверждение, или, может быть, я действительно запутался и все перепутал, но если кто-то может объяснить.

Спасибо за ваше время заранее.

2 ответов


питоны грамматики

к счастью, есть полная спецификация грамматики в документации Python.

оператор определяется в этой спецификации как:

stmt: simple_stmt | compound_stmt

и логическая строка ограничена NEWLINE (это не в спецификации, но на основе вашего вопроса).

шаг за шагом

Хорошо, давайте пройдем через это, какова спецификация для

simple_stmt:

simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)

хорошо, теперь он переходит в несколько разных путей, и, вероятно, не имеет смысла проходить через все из них отдельно, но на основе спецификации a simple_stmt мог бы пересечь границы логической линии если любой small_stmts содержит NEWLINE (в настоящее время они не но мог бы).

помимо этой единственной теоретической возможности на самом деле существует

compound_stmt:

compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
[...]
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
[...]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

я выбрал только if заявления и suite потому что этого уже достаточно. The if сообщении в том числе elif и else и все содержание в этих одно заявление (составной оператор). И потому что он может содержать NEWLINEs (Если suite не просто simple_stmt) он уже выполняет требование " утверждения, которое пересекает логическую линию границы."

пример if (схематично):

if 1:
    100
    200

будет:

if_stmt
|---> test        --> 1
|---> NEWLINE
|---> INDENT
|---> expr_stmt   --> 100
|---> NEWLINE
|---> expr_stmt   --> 200
|---> NEWLINE
|---> DEDENT

и все это принадлежит оператору if (и это не просто блок, "контролируемый"if или while, ...).

то же самое if С parser, symbol и token

способ визуализации, который будет использовать встроенный parser, token и symbol модули (действительно, я не знал об этих модулях, прежде чем написал ответ):

import symbol
import parser
import token

s = """
if 1:
    100
    200
"""
st = parser.suite(s)

def recursive_print(inp, level=0):
    for idx, item in enumerate(inp):
        if isinstance(item, int):
            print('.'*level, symbol.sym_name.get(item, token.tok_name.get(item, item)), sep="")
        elif isinstance(item, list):
            recursive_print(item, level+1)
        else:
            print('.'*level, repr(item), sep="")

recursive_print(st.tolist())

на самом деле я не могу объяснить большинство parser результат, но он показывает (если вы удалите много ненужных строк), что suite в том числе это newlines действительно принадлежит if_stmt. Отступ представляет "глубину" синтаксического анализатора в определенной точке.

file_input
.stmt
..compound_stmt
...if_stmt
....NAME
....'if'
....test
.........expr
...................NUMBER
...................'1'
....COLON
....suite
.....NEWLINE
.....INDENT
.....stmt
...............expr
.........................NUMBER
.........................'100'
.......NEWLINE
.....stmt
...............expr
.........................NUMBER
.........................'200'
.......NEWLINE
.....DEDENT
.NEWLINE
.ENDMARKER

это, вероятно, можно было бы сделать намного красивее, но я надеюсь, что это служит иллюстрацией даже в нынешняя форма.


это проще, чем вы думаете. Составной оператор считается одним оператором, даже если внутри него могут находиться другие операторы. Цитируя docs:

составные операторы содержат (группы) других операторов; они каким-то образом влияют или контролируют выполнение этих других операторов. в общем, составные операторы охватывают несколько строк, хотя в простых воплощениях целое составное утверждение может содержаться в одном линия.

например,

if a < b:
    do_thing()
    do_other_thing()

один if оператор, занимающий 3 логические строки. Вот как утверждение может пересекать границы логической линии.