Вот примерная версия того, что можно сделать:
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
Обман в том, что я получаю точно такой же вывод, если я переставляю части 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
и SinOfThird
).Дайте мне знать, если вам нужен исходный код graphexecute
, я не включил его здесь, потому что он содержит много вещей, которые не имеют отношения к этому вопросу.