首页
学习
活动
专区
圈层
工具
发布

Python compiler package

自2.6版弃用:该compiler软件包已在Python 3中删除。

Python编译器包是用于分析Python源代码并生成Python字节码的工具。编译器包含用于从Python源代码生成抽象语法树并从树中生成Python 字节码的库。

该compiler软件包是用Python编写的字节码翻译器的Python源代码。它使用内置的解析器和标准parser模块来生成具体的语法树。该树用于生成抽象语法树(AST),然后生成Python字节码。

该包的全部功能与Python解释器提供的内置编译器相同。它旨在几乎完全匹配它的行为。为什么要实现另一个编译器来做同样的事情?该软件包适用于各种用途。它可以比内置编译器更容易修改。它生成的AST对分析Python源代码很有用。

本章介绍了该compiler软件包的各个组件如何工作。它将参考材料与教程混合在一起。

1.基本界面

包的顶层定义了四个函数。如果您导入compiler,您将获得这些功能和包中包含的模块集合。

compiler.parse(buf)

返回buf中 Python源代码的抽象语法树。SyntaxError如果源代码中有错误,则会引发该函数。返回值是compiler.ast.Module包含树的实例。

compiler.parseFile(path)

在由path指定的文件中返回Python源代码的抽象语法树。这相当于parse(open(path).read())。

compiler.walk(ast, visitor[, verbose])

做一个预先遍历的抽象语法树ast。在每个遇到的节点的访问者实例上调用适当的方法。

compiler.compile(source, filename, mode, flags=None, dont_inherit=None)

将字符串源(一个Python模块,语句或表达式)编译成可由exec语句或.exe执行的代码对象eval()。该功能是内置功能的替代品compile()。

该文件名将用于运行时错误消息。

该模式必须是“EXEC”编译一个模块,“单”编译一个单一(交互式)语句或“EVAL”编译的表达式。

该标志和dont_inherit参数影响未来相关的语句,但尚不支持。

compiler.compileFile(source)

编译文件源并生成.pyc文件。

该compiler包包含以下模块:ast,consts,future,misc,pyassem,pycodegen,symbols,transformer,和visitor。

2.限制

编译器包的错误检查有一些问题。解释器检测两个不同阶段的语法错误。解释器的解析器检测到一组错误,另一组错误由编译器设置。编译器包依赖于解释器的解析器,因此它可以免费获得错误检查的第一阶段。它实现了第二阶段本身,并且实现是不完整的。例如,如果名称在参数列表中多次出现一次,则编译器程序包不会引发错误:def f(x, x): ...

未来版本的编译器应该解决这些问题。

3. Python抽象语法

该compiler.ast模块为Python定义了一个抽象语法。在抽象语法树中,每个节点表示一个语法结构。树的根是Module对象。

抽象语法为解析的Python源代码提供更高级别的接口。parser用C编写的用于Python解释器的模块和编译器使用具体的语法树。具体语法与用于Python解析器的语法描述紧密相关。Python的优先规则引入了多层嵌套节点,而不是单个节点。

抽象语法树由compiler.transformer模块创建。变换器依靠内置的Python解析器来生成具体的语法树。它从具体树中生成一个抽象语法树。

该transformer模块由Greg Stein和Bill Tutt为实验性Python-to-C编译器创建。当前版本包含许多修改和改进,但抽象语法和变压器的基本形式归功于Stein和Tutt。

3.1.AST节点

该compiler.ast模块由描述每个节点类型及其元素的文本文件生成。每个节点类型都表示为从抽象基类继承的类,compiler.ast.Node并为子节点定义一组命名属性。

class compiler.ast.Node

这些Node实例由解析器生成器自动创建。推荐的特定Node实例接口是使用公共属性来访问子节点。公共属性可以绑定到单个节点或节点序列,具体取决于Node类型。例如,节点的bases属性Class绑定到基类节点列表,并且该doc属性绑定到单个节点。

每个Node实例都有一个lineno可能的属性None。XXX不知道哪些节点会有一个有用的lineno规则。

所有Node对象都提供以下方法:

getChildren()

按照它们发生的顺序返回子节点和对象的展开列表。具体来说,节点的顺序是它们在Python语法中出现的顺序。并非所有的孩子都是Node实例。例如,函数和类的名称是纯字符串。

getChildNodes()

按照它们发生的顺序返回一个扁平化的子节点列表。这种方法就像getChildren(),只是它只返回那些Node实例的孩子。

两个例子说明了Node类的一般结构。该while声明由以下语法生成定义:

代码语言:javascript
代码运行次数:0
复制
while_stmt:     "while" expression ":" suite
               ["else" ":" suite]

该While节点有三个属性:test,body,和else_。(如果属性的自然名称也是Python保留字,则不能将其用作属性名称。下划线会附加到单词上以使其成为合法标识符,因此else_不是else。)

该if声明是比较复杂的,因为它可以包括多个测试。

代码语言:javascript
代码运行次数:0
复制
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]

该If节点只定义两个属性:tests和else_。该tests属性是一系列测试表达式,随后的身体对。每个if/ elif子句有一对。这一对的第一个元素是测试表达式。第二个元素是Stmt包含要在测试为真时执行的代码的节点。

该getChildren()方法If返回子节点的平面列表。如果有三个if/ elif子句并且没有else子句,那么getChildren()将返回一个包含六个元素的列表:第一个测试表达式,第一个Stmt,第二个文本表达式等。

下表列出了其实例中可用的每个公共属性和每个公共属性中Node定义的compiler.ast每个子类。大多数属性的值本身就是Node实例或实例序列。当该值不是实例时,该类型将在注释中标出。这些属性按照getChildren()和返回它们的顺序列出getChildNodes()。

节点类型

属性

值

Add

left

左操作数

​

right

右操作数

And

nodes

操作数列表

AssAttr

​

属性作为分配的目标

​

expr

表达式在点的左侧

​

attrname

属性名称,一个字符串

​

flags

XXX

AssList

nodes

要分配的列表元素列表

AssName

name

名称被分配给

​

flags

XXX

AssTuple

nodes

要分配的元组元素列表

Assert

test

表达式进行测试

​

fail

AssertionError的值

Assign

nodes

分配目标列表,每个等号一个

​

expr

该值被分配

AugAssign

node

​

​

op

​

​

expr

​

Backquote

expr

​

Bitand

nodes

​

Bitor

nodes

​

Bitxor

nodes

​

Break

​

​

CallFunc

node

表达式为被调用者

​

args

参数列表

​

star_args

扩展的* -arg值

​

dstar_args

扩展的** - 参数值

Class

name

类的名称,一个字符串

​

bases

基类的列表

​

doc

doc字符串,一个字符串或None

​

code

类声明的主体

Compare

expr

​

​

ops

​

Const

value

​

Continue

​

​

Decorators

nodes

函数装饰器表达式列表

Dict

items

​

Discard

expr

​

DIV

left

​

​

right

​

Ellipsis

​

​

Expression

node

​

Exec

expr

​

​

locals

​

​

globals

​

FloorDiv

left

​

​

right

​

For

assign

​

​

list

​

​

body

​

​

else_

​

From

modname

​

​

names

​

Function

decorators

装饰者或无

​

name

在def中使用的名称,一个字符串

​

argnames

参数名称列表,作为字符串

​

flags

默认值列表

​

flags

XXX

​

doc

doc字符串,一个字符串或None

​

code

函数的主体

GenExpr

code

​

GenExprFor

assign

​

​

iter

​

​

ifs

​

GenExprIf

test

​

GenExprInner

expr

​

​

quals

​

GETATTR

expr

​

​

attrname

​

Global

names

​

If

tests

​

​

else_

​

Import

names

​

Invert

expr

​

Keyword

name

​

​

expr

​

Lambda

argnames

​

​

defaults

​

​

flags

​

​

code

​

LeftShift

left

​

​

right

​

List

nodes

​

ListComp

expr

​

​

quals

​

ListCompFor

assign

​

​

list

​

​

ifs

​

ListCompIf

test

​

Mod

left

​

​

right

​

Module

doc

doc字符串,一个字符串或None

​

node

一个Stmt模块的主体

Mul

left

​

​

right

​

Name

name

​

不

expr

​

Or

nodes

​

Pass

​

​

Power

left

​

​

right

​

Print

nodes

​

​

dest

​

Printnl

nodes

​

​

dest

​

Raise

expr1

​

​

expr2

​

​

expr3

​

Return

value

​

RightShift

left

​

​

right

​

Slice

expr

​

​

flags

​

​

lower

​

​

upper

​

Sliceobj

nodes

报表清单

Stmt

nodes

​

Sub

left

​

​

right

​

Subscript

expr

​

​

flags

​

​

subs

​

TryExcept

body

​

​

handlers

​

​

else_

​

TryFinally

body

​

​

final

​

Tuple

nodes

​

UnaryAdd

expr

​

UnarySub

expr

​

While

test

​

​

body

​

​

else_

​

With

expr

​

​

vars

​

​

body

​

Yield

value

​

3.2.分配节点

有一组节点用于表示分配。源代码中的每个赋值语句都成为AssignAST中的单个节点。该nodes属性是一个列表,其中包含每个分配目标的节点。这是必要的,因为分配可以链接,例如a = b = 2。每个Node列表中的将是以下的一类:AssAttr,AssList,AssName,或AssTuple。

每个目标分配节点将描述分配给的对象类型:AssName简单名称,例如a = 1。AssAttr对于分配的属性,例如a.x = 1。AssList并分别AssTuple用于列表和元组扩展,例如a, b, c = a_tuple。

目标分配节点还具有一个flags属性,用于指示节点是用于分配还是用于删除语句。该AssName也被用来代表一个delete语句,例如del x。

当表达式包含多个属性引用时,赋值语句或删除语句将只包含一个AssAttr节点 - 用于最终的属性引用。其他属性引用将表示为实例属性中的Getattr节点。exprAssAttr

3.3.例子

本节展示了Python源代码AST的几个简单例子。这些示例演示了如何使用该parse()函数,AST的repr是什么样的,以及如何访问AST节点的属性。

第一个模块定义了一个单一功能。假设它存储在中doublelib.py。

代码语言:javascript
代码运行次数:0
复制
"""This is an example module.

This is the docstring.
"""

def double(x):
    "Return twice the argument"
    return x * 2

在下面的交互式口译员会议中,为了便于阅读,我已经重新格式化了长期的AST代表。AST报告人使用不合格的类名。如果您想从repr创建实例,则必须从compiler.ast模块中导入类名称。

代码语言:javascript
代码运行次数:0
复制
>>> import compiler
>>> mod = compiler.parseFile("doublelib.py")
>>> mod
Module('This is an example module.\n\nThis is the docstring.\n',
       Stmt([Function(None, 'double', ['x'], [], 0,
                      'Return twice the argument',
                      Stmt([Return(Mul((Name('x'), Const(2))))]))]))
>>> from compiler.ast import *
>>> Module('This is an example module.\n\nThis is the docstring.\n',
...    Stmt([Function(None, 'double', ['x'], [], 0,
...                   'Return twice the argument',
...                   Stmt([Return(Mul((Name('x'), Const(2))))]))]))
Module('This is an example module.\n\nThis is the docstring.\n',
       Stmt([Function(None, 'double', ['x'], [], 0,
                      'Return twice the argument',
                      Stmt([Return(Mul((Name('x'), Const(2))))]))]))
>>> mod.doc
'This is an example module.\n\nThis is the docstring.\n'
>>> for node in mod.node.nodes:
...     print node
...
Function(None, 'double', ['x'], [], 0, 'Return twice the argument',
         Stmt([Return(Mul((Name('x'), Const(2))))]))
>>> func = mod.node.nodes[0]
>>> func.code
Stmt([Return(Mul((Name('x'), Const(2))))])

4.使用访问者走AST

访问者模式是...该compiler软件包在访问者模式中使用了一种变体,它利用了Python的内省功能来消除对访问者基础设施的需求。

被访问的课程不需要编程来接受访问者。访问者只需要定义它特别感兴趣的类的访问方法; 一个默认的访问方法可以处理其余的。

XXX visit()为参观者提供的神奇方法。

compiler.visitor.walk(tree, visitor[, verbose])class compiler.visitor.ASTVisitor

该ASTVisitor负责以正确的顺序走在树。散步从打电话开始preorder()。对于每个节点,它都会检查访问者参数以preorder()找到名为'visitNodeType'的方法,其中NodeType是节点类的名称,例如,将调用While节点a visitWhile()。如果该方法存在,则将该节点作为其第一个参数进行调用。

特定节点类型的访问者方法可以控制在步行过程中子节点的访问方式。通过ASTVisitor向访问者添加访问方法来修改访问者参数; 此方法可用于访问特定的子节点。如果找不到特定节点类型的访客,default()则调用该方法。

ASTVisitor 对象有以下方法:

XXX描述额外的参数

default(node[, ...])dispatch(node[, ...])preorder(tree, visitor)

5.字节码生成

代码生成器是一个发出字节码的访问者。每个访问方法都可以调用该emit()方法来发出一个新的字节码。基本代码生成器专门用于模块,类和函数。汇编程序将发送的指令转换为低级字节码格式。它处理诸如生成代码对象的常量列表和计算跳转偏移量等内容。

扫码关注腾讯云开发者

领取腾讯云代金券