我想创造一个象征性的"函数调用的表示(在我看来,表示这个的最好方法是使用Graph或DAG或Tree),即设置执行图,然后稍后执行图,可能输入略有不同或图在执行时具有完全不同的状态(类似于TensorFlow如何执行它,使用图形的输入和输入)。
例如,如果我有:
graph = f( g('a','b', h('c','d') ), seed )
我想要图表:
并且基本上可以控制我如何执行图表:
graph.execute()
我觉得这是某种设计模式,因为说TensorFlow,Mathematica和SymPy似乎都在使用(注意它似乎与语言无关)。虽然,我很难找到设计模式的名称,这样我就可以在Python中使用自己的执行图节点和数据类型来实现它。有谁知道如何做到这一点或有一个很好的链接到设计模式的名称,以便我可以建立这个?
对我来说重要的是图形创建就像TensorFlow,SymPy等一样简单。换句话说。我希望我可以使用多种语法来创建相同的图形。例如,它不应该很难做到:
h = h('c','d')
g = g('a','b', h )
graph = f( g, seed )
如果用户想要。
答案 0 :(得分:4)
这是一个简单的例子:
# dataflow constructs
#####################
class DelayedFunctionCall:
def __init__(self, func, *args):
self.func = func
self.args = args
def execute(self):
args = [arg.execute() if isinstance(arg, type(self)) else arg for arg in self.args]
return self.func(*args)
def dataflow(func):
return lambda *args: DelayedFunctionCall(func, *args)
具体来说,我创建dataflow
作为python函数的包装器,将函数和参数存储到DelayedFunctionCall
。
这个机制只是存储函数和参数,但是还没有执行。
从execute()
调用DelayedFunctionCall
实际上解析了存储的函数和参数。请注意DelayedFunctionCall
注意解决它首先收到的任何DelayedFunctionCall
个参数(通过调用arg.execute()
)。
# user code
###########
@dataflow
def f(g, seed):
return g**2 % seed
@dataflow
def g(a, b, h):
return a * b + h
@dataflow
def h(c, d):
return c / d
seed = 5
# setting up the execution / dataflow
graph = f(g(1, 2, h(3, 4)), seed)
# no mathematical operations have happened yet; i.e. bodies of f,g,h functions have not been executed yet
# executing the dataflow
print(graph.execute())
请注意使用@dataflow
装饰器。如果您愿意,还可以定期定义函数,然后将它们转换为DelayedFunctionCall
s:
def f(g, seed):
return g**2 % seed
# do stuff with regular, non-dataflow f
f = dataflow(f) # now f is a DelayedFunctionCall
您可以在github.com/pcattori/dataflow
上查看代码(支持**kwargs
以及将绑定变量延迟到值的执行时间!)
答案 1 :(得分:1)
我相信我理解你想要的东西,这是定义图形的语法糖。
结果代码非常类似于Pedro Cattori
主要区别在于您无需在定义图表之前定义Input
。
我改变的其他小事是:
在Function
中重命名数据流通过wraps()
调用
使用hasattr()
代替实例测试以允许其他类,例如Input()
。
import functools
class Input():
def set(self, value):
self.value = value
def execute(self):
return self.value
def Function(f):
@functools.wraps(f)
def g(*args,**kwargs):
return Executor(f,args)
return g
class Executor():
def __init__(self,f,args):
self.f = f
self.args = args
def execute(self):
return self.f(*(arg.execute() if hasattr(arg,"execute") else arg
for arg in self.args))
@Function
def f(g, seed):
return g**2 % seed
@Function
def g(a, b, h):
return a * b + h
@Function
def h(c, d):
return c / d
seed = Input()
# setting up the execution / dataflow
graph = f(g(1, 2, h(3, 4)), seed)
#you can also do in several steps
H = h(3,4)
graph2 = f(g(1,2,H),seed)
#inputs value can be late binded
seed.set(5)
# executing the dataflow
print(graph.execute()) #2.5625
#both way to the definition give same result
print(graph2.execute()) #2.5625