Какова связь между объектом кода python и абстрактным синтаксическим деревом?

#python #abstract-syntax-tree

#python #абстрактное синтаксическое дерево

Вопрос:

Я понимаю, что компилятор python компилирует код python в объекты кода, которые содержат байт-код и несколько других полей, таких как непосредственные значения в блоке кода, переменные, stack_sz и т.д. Затем интерпретатор сопоставляет объекты кода с функциями / предварительно скомпилированными сборками c.

У меня возникли проблемы с соединением этого поведения python с тем, что я узнал в своем классе компилятора. В общем, компиляторы создают некоторое промежуточное представление исходного кода и сохраняют его в структуре данных, такой как абстрактное синтаксическое дерево.

Мои вопросы: какова связь между объектом кода python и AST? Если вы выполняете генерацию кода в AST, является ли объект кода тем, что вы получаете? Можно ли просматривать AST и вручную генерировать объекты кода с помощью команд python?

Комментарии:

1. Как и большинство компиляторов, Python использует AST на этапе синтаксического анализа. Вы можете создать их самостоятельно, используя ast модуль.

2. Python позволяет вам манипулировать объектами AST. Например, Pytest делает это для перезаписи утверждений.

3. AST — это входные данные для шага, который создает объект кода. Встроенная compile функция позволяет передавать исходный код в виде текста (в этом случае сначала создается AST) или AST в качестве входных данных и возвращать объект кода.

4. @gilch Это интересно! Я никогда не думал об этом. Не могли бы вы немного рассказать о том, как AST используется Pytest?

5. Перезапись утверждения Pytest

Ответ №1:

Мои вопросы: какова связь между объектом кода python и AST?

AST — это промежуточный этап в процессе компиляции кода Python. Вы можете создать байт-код Python из AST и создать байт-код Python из кода Python с помощью AST.

 >>> import ast
>>> my_ast = ast.parse("print('hi')", "<str>", "exec")
>>> my_ast
<_ast.Module object at 0x000001616E96B9A0>
 

Если вы выполняете генерацию кода в AST, является ли объект кода тем, что вы получаете?

 >>> compile(my_ast, "<str>", "exec")
<code object <module> at 0x000001616EA262F0, file "<str>", line 1>
 

Можно ли просматривать AST и вручную генерировать объекты кода с помощью команд python?

 >>> ast.dump(my_ast)
"Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Constant(value='hi', kind=None)], keywords=[]))], type_ignores=[])"
 

Смотрите Учебное пособие Green Tree Snakes о том, как управлять ast.

Стандартная библиотека AST tools значительно улучшилась в версии Python 3.9. Теперь он может печатать дампы и анализировать ast обратно в строку кода.

Ответ №2:

AST является промежуточным шагом между исходным кодом и объектом кода. Встроенная compile функция может создавать code объект из любого исходного кода:

 >>> import ast, dis
>>> program = "3   x"
>>> dis.dis(compile(program, "", "eval"))
  1           0 LOAD_CONST               0 (3)
              2 LOAD_NAME                0 (x)
              4 BINARY_ADD
              6 RETURN_VALUE
 

(в этом случае он просто сначала анализирует исходный код в AST)
или непосредственно из существующего AST:

 >>> program_ast = ast.dump(ast.parse(program, "", "eval"))
>>> program_ast
"Expression(body=BinOp(left=Constant(value=3, kind=None), op=Add(), right=Name(id='x', ctx=Load())))"
>>> dis.dis(compile(program_ast, "", "eval"))
  1           0 LOAD_CONST               0 (3)
              2 LOAD_NAME                0 (x)
              4 BINARY_ADD
              6 RETURN_VALUE
 

В обоих случаях dis.dis показывает, что конечный результат один и тот же, независимо от того, к чему вы переходите compile .


(Фактически, ast.parse это то же самое, что передать PyCF_ONLY_AST флаг compile , чтобы вы получили дерево синтаксического анализа вместо конечного байтового кода.)

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

 >>> ast.dump(ast.parse("3   5", "", "eval"))
'Expression(body=BinOp(left=Constant(value=3, kind=None), op=Add(), right=Constant(value=5, kind=None)))'
 

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

 'Expression(body=Constant(value=8, kind=None))'
 

без изменения семантики конечной программы.