[364]python ast模块

我们知道python解析执行的过程大概是这样的:

词法分析 --> 具体语法树 --> 抽象语法树 --> 控制流图 --> 字节码 --> 执行

其中,抽象语法树可以用ast模块建立出来.

首先第一个问题是, 这个ast模块有什么用呢? ast提供了访问和修改上述中抽象语法树的功能.可以做一些比如测试,代码生成,静态分析等等. 比如pylint, pythonscope就用到这个功能.

ast这个module提供了一些访问节点的接口:

ast.iter_fields(node)

Yield a tuple of (fieldname, value) for each field in node._fields that is present on node.

ast.iter_child_nodes(node

Yield all direct child nodes of node, that is, all fields that are nodes and all items of fields that are lists of nodes.

ast.walk(node)

Recursively yield all descendant nodes in the tree starting at node (including node itself), in no specified order. This is useful if you only want to modify nodes in place and don’t care about the context.

这里只列出了几个遍历的函数.如果要遍历整个树呢?

class ast.NodeVisitor 和 class ast.NodeTransformer

这两个的区别就是visit是修改原来的node,transformer可以替换一个新的node.

那么我们现在做一个功能,把py文件中的字符串 “123456” 全部替换为 “45678”

class MyVisitor(ast.NodeVisitor):
    def visit_Str(self, node):
        print 'Found string "%s"' % node.s
        if node.s == "123456":
            node.s = "45678"
    
    def visit_FunctionDef(self, node):
        print 'Found function ', node._fields
    

MyVisitor().visit(mast)

import codegen
print codegen.to_source(mast)

查看输出:

a = 1
b = 2
c = a + b


class c1(object):
    c_var_int = 1
    c_var_str = '45678'

    def __init__(self):
        return

    def func1(self, x, y):
        return

已经替换.

visitor的遍历方法就是对于每一个node, 去找 visit_node类名 这个函数,找到了就访问,找不到就找generic_visit这个函数.

transform也是一样.

好,现在我们做这样一个事情.对于某个class, 在发生函数调用的时候,打出一条log,这种情况在调试的时候是很常见的吧.那么这个可以用getattr来做,就是getattr的时候判断是不是一个函数,如果是函数就打出log,这样很好可是不能打出参数.也可以用decorator来做,就比较麻烦,就要挨个去加.

源文件是这样的:

class c1(object):
    c_var_int = 1 
    c_var_str = "123456"
    def __init__(self):
        return
    
    def func1(self, x, y): 
        c = x + y 
        return c



class c2(c1, object):
    pass

def gfunc1(x, y): 
    return x*x + y*y 

获取语法树

ast模块的基本使用是非常简单的,我们可以通过如下代码快速得到一棵抽象语法树:

import ast
root_node = ast.parse("print 'hello world'")
root_node -> <_ast.Module object at 0x9e3df6c>

通过ast的parse方法得到ast tree的根节点root_node, 我看可以通过根节点来遍历语法树,从而对python代码进行分析和修改。

ast.parse(可以直接查看ast模块的源代码)方法实际上是调用内置函数compile进行编译,如下所示:

def parse(source, filename='', mode='exec'):
    """
    Parse the source into an AST node.
    Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
    """
    return compile(source, filename, mode, PyCF_ONLY_AST)

传递给compile特殊的flag = PyCF_ONLY_AST, 来通过compile返回抽象语法树。


eval和ast.literal_val()的区别

eval函数在Python中做数据类型的转换还是很有用的。它的作用就是把数据还原成它本身或者是能够转化成的数据类型。

eval在做计算前并不知道需要转化的内容是不是合法的(安全的)python数据类型。只是在调用函数的时候去计算。如果被计算的内容不是合法的python类型就会抛出异常。

ast.literal则会判断需要计算的内容计算后是不是合法的python类型,如果是则进行运算,否则就不进行运算。

因此,推荐使用ast.literal_eval

JSON 的标准:双引号而非单引号!

这个问题是由于使用json.loads报错写的,这个时候可尝试ast.literal_eval

参考:https://zhuanlan.zhihu.com/p/21945624
https://blog.csdn.net/ma89481508/article/details/56017697
https://www.jianshu.com/p/e8faf975a22d

你可能感兴趣的:(python前行者)