下面是可以做的事情的大致版本:
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
来自if
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
关键是,如果我对
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-算法
并将其转换为一个AST(2)修补中定义的每个函数
My-算法
(例如,sumfirstarguments)返回局部变量(3)根据输入和输出(由类型提示定义)决定
My-算法
应该执行。此外,我还没有实现的可能性是并行执行独立的部分(例如
SumFirstArguments
和
SinOfThird
)如果您需要的源代码,请通知我
graphexecute
,我没有把它包括在这里,因为它包含了很多与这个问题无关的东西。