python - Python:猴子修补函数的源代码
问题描述
我可以在函数的源代码中添加前缀和后缀吗?
我知道装饰器并且不想使用它们(下面的最小示例没有说明原因,但我有我的理由)。
def f():
print('world')
g = patched(f,prefix='print("Hello, ");',suffix='print("!");')
g() # Hello, world!
这是我到目前为止所拥有的:
import inspect
import ast
import copy
def patched(f,prefix,suffix):
source = inspect.getsource(f)
tree = ast.parse(source)
new_body = [
ast.parse(prefix).body[0],
*tree.body[0].body,
ast.parse(suffix).body[0]
]
tree.body[0].body = new_body
g = copy.deepcopy(f)
g.__code__ = compile(tree,g.__code__.co_filename,'exec')
return g
不幸的是,如果我使用它然后g()
像上面那样调用,什么也不会发生;既不打印world
也不Hello, world!
打印。
解决方案
这是可以做的粗略版本:
import inspect
import ast
import copy
def patched(f,prefix,suffix):
source = inspect.getsource(f)
tree = ast.parse(source)
new_body = [
ast.parse(prefix).body[0],
*tree.body[0].body,
ast.parse(suffix).body[0]
]
tree.body[0].body = new_body
code = compile(tree,filename=f.__code__.co_filename,mode='exec')
namespace = {}
exec(code,namespace)
g = namespace[f.__name__]
return g
def temp():
pass
def f():
print('world',end='')
g = patched(f,prefix='print("Hello, ",end="")',suffix='print("!",end="")')
g() # Hello, world!
的调用compile
编译整个模块(由 表示tree
)。然后在一个空的命名空间中执行该模块,最终从中提取所需的功能。(警告:命名空间将需要填充一些全局变量,f
如果f
使用这些全局变量。)
经过更多的工作,这里是一个真实的例子,可以用这个来做些什么。它使用了上述原理的一些扩展版本:
import numpy as np
from playground import graphexecute
@graphexecute(verbose=True)
def my_algorithm(x,y,z):
def SumFirstArguments(x,y)->sumxy:
sumxy = x+y
def SinOfThird(z)->sinz:
sinz = np.sin(z)
def FinalProduct(sumxy,sinz)->prod:
prod = sumxy*sinz
def Return(prod):
return prod
print(my_algorithm(x=1,y=2,z=3))
#OUTPUT:
#>>Executing part SumFirstArguments
#>>Executing part SinOfThird
#>>Executing part FinalProduct
#>>Executing part Return
#>>0.4233600241796016
clou 是,如果我重新调整 的部分,我会得到完全相同的输出my_algorithm
,例如:
@graphexecute(verbose=True)
def my_algorithm2(x,y,z):
def FinalProduct(sumxy,sinz)->prod:
prod = sumxy*sinz
def SumFirstArguments(x,y)->sumxy:
sumxy = x+y
def SinOfThird(z)->sinz:
sinz = np.sin(z)
def Return(prod):
return prod
print(my_algorithm2(x=1,y=2,z=3))
#OUTPUT:
#>>Executing part SumFirstArguments
#>>Executing part SinOfThird
#>>Executing part FinalProduct
#>>Executing part Return
#>>0.4233600241796016
这通过 (1) 获取源my_algorithm
并将其转换为 ast (2) 修补在my_algorithm
(例如 SumFirstArguments) 中定义的每个函数以返回本地 (3) 根据输入和输出进行决定(由类型提示定义)的部分my_algorithm
应按什么顺序执行。此外,我还没有实现的一种可能性是并行执行独立的部分(例如SumFirstArguments
and SinOfThird
)。如果您想要 的源代码,请graphexecute
告诉我,我没有在此处包含它,因为它包含很多与此问题无关的内容。
推荐阅读
- windows - 使用 bitbucket 编辑文件会将其添加到文件的开头:M-oM-;M-?
- ios - 你能制定一个强制视图控制器触发特定自定义 NSNotifications 的 Swift 协议吗?
- python-3.x - 有没有办法在不下载的情况下获取 Google Drive 上 zip 文件的内容列表?
- c - 如何在 omnet.ini 文件中查找 manetRouter 的可用参数列表
- python - Selenium Python,如何使用 selenium 控制已经打开的网页
- sql - 根据时间戳删除重复记录
- reactjs - 在 immer 生产者中创建状态的深层副本的最佳方法是什么
- java - 如何将整数值转换为双十进制值?
- php - 在 C 中进行数字签名并在 PHP 中进行验证的正确算法是什么?
- serverless - OpenWhisk 调用程序代理暂停时间