Python compiler package
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
声明由以下语法生成定义:
while_stmt: "while" expression ":" suite
["else" ":" suite]
该While
节点有三个属性:test
,body
,和else_
。(如果属性的自然名称也是Python保留字,则不能将其用作属性名称。下划线会附加到单词上以使其成为合法标识符,因此else_
不是else
。)
该if
声明是比较复杂的,因为它可以包括多个测试。
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 | |
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.分配节点
有一组节点用于表示分配。源代码中的每个赋值语句都成为Assign
AST中的单个节点。该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
。
"""This is an example module.
This is the docstring.
"""
def double(x):
"Return twice the argument"
return x * 2
在下面的交互式口译员会议中,为了便于阅读,我已经重新格式化了长期的AST代表。AST报告人使用不合格的类名。如果您想从repr创建实例,则必须从compiler.ast
模块中导入类名称。
>>> 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()
方法来发出一个新的字节码。基本代码生成器专门用于模块,类和函数。汇编程序将发送的指令转换为低级字节码格式。它处理诸如生成代码对象的常量列表和计算跳转偏移量等内容。