当前位置:   article > 正文

python 通过 ast 替换代码_ast替换函数

ast替换函数

目录结构

.
├── hello
│   ├── __init__.py
│   └── utils.py
├── main.py
├── replace_code.py
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

要替换的代码

代码在文件 utils.py 里面

def sum(a: int, b: int) -> int:
    c = a - b
    return c
  • 1
  • 2
  • 3

替换代码的逻辑

替换代码逻辑在文件 replace_code.py 里

新建类,继承 ast

逻辑很简单:找到哪一行并替换,就是中间的判断可能比较多

import ast
class MyTransformer(ast.NodeTransformer):
    def visit(self, node: ast.AST):
    	# 我们要找到要替换的那一行('c = a - b')代码在哪里
        for nodei, v in enumerate(node.body):
            if isinstance(v, ast.FunctionDef):
                for i, vv in enumerate(v.body):
                    if isinstance(vv, ast.Assign) and \
                    isinstance(vv.value, ast.BinOp) and \
                    isinstance(vv.value.left, ast.Name) and \
                    vv.value.left.id == 'a' and vv.value.right.id == 'b' and isinstance(vv.value.op, ast.Sub):# 找到了那一行代码
                        # 找到后把新的代码通过 ast.parse 转换成语法树,并替换掉原来的代码
                        v.body[i] = ast.parse('c = a + b').body[0]
                        node.body[nodei] = v
                    
        return node
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
运行新建的类
from hello import utils
import inspect
# 获取代码内容
method_code = inspect.getsource(utils.sum)
print(f'----sum code begin\n {method_code} \n -----sum code end ----')
# 把要替换的代码转换成语法树
method_ast = ast.parse(method_code)
trans_instance = MyTransformer()
# 运行新建的类的里面的逻辑
transformed_ast = trans_instance.visit(method_ast)
# 把语法树解析成语法代码
transformed_code = ast.unparse(transformed_ast)
print('--- transformed code:', transformed_code)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
最最重要的一步

一定要把代码在它所在的命名空间里执行它

exec(transformed_code, utils.__dict__)
  • 1
replace_code.py 完整代码
import ast
class MyTransformer(ast.NodeTransformer):
    def visit(self, node: ast.AST):
        for nodei, v in enumerate(node.body):
            if isinstance(v, ast.FunctionDef):
                for i, vv in enumerate(v.body):
                    if isinstance(vv, ast.Assign) and \
                    isinstance(vv.value, ast.BinOp) and \
                    isinstance(vv.value.left, ast.Name) and \
                    vv.value.left.id == 'a' and vv.value.right.id == 'b' and isinstance(vv.value.op, ast.Sub):
                        print('here')
                        v.body[i] = ast.parse('c = a + b').body[0]
                        node.body[nodei] = v
                    
        return node
from hello import utils
import inspect
method_code = inspect.getsource(utils.sum)
print(f'----sum code begin\n {method_code} \n -----sum code end ----')
method_ast = ast.parse(method_code)
trans_instance = MyTransformer()
transformed_ast = trans_instance.visit(method_ast)
transformed_code = ast.unparse(transformed_ast)
print('--- transformed code:', transformed_code)
exec(transformed_code, utils.__dict__)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

main.py 里面的代码

导入的顺序一定不要错了,不然也不会生效的

# 引入的顺序一定不要错了,不然也不会生效的
import replace_code
from hello.utils import sum
if __name__ == '__main__':
    print(sum(2,1))
  • 1
  • 2
  • 3
  • 4
  • 5

执行结果

----sum code begin
 def sum(a: int, b: int) -> int:
    c = a - b
    return c
 
 -----sum code end ----

--- transformed code: def sum(a: int, b: int) -> int:
    c = a + b
    return c
3 # 这是执行后的结果
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最后

逻辑代码不一定要在 visit() 里面写,如果你要修改的是 for 循环里面的代码 你可以重写 visit_For()方法, 在里面来实现自己的逻辑

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/597626
推荐阅读
相关标签
  

闽ICP备14008679号