如何创建一个未在Python中立即求值的符号函数执行DAG?

时间:2017-04-12 19:46:13

标签: python design-patterns graph tensorflow sympy

我想创造一个象征性的"函数调用的表示(在我看来,表示这个的最好方法是使用Graph或DAG或Tree),即设置执行图,然后稍后执行图,可能输入略有不同或图在执行时具有完全不同的状态(类似于TensorFlow如何执行它,使用图形的输入和输入)。

例如,如果我有:

graph = f( g('a','b', h('c','d') ), seed )

我想要图表:

enter image description here

并且基本上可以控制我如何执行图表:

graph.execute()

我觉得这是某种设计模式,因为说TensorFlow,Mathematica和SymPy似乎都在使用(注意它似乎与语言无关)。虽然,我很难找到设计模式的名称,这样我就可以在Python中使用自己的执行图节点和数据类型来实现它。有谁知道如何做到这一点或有一个很好的链接到设计模式的名称,以便我可以建立这个?

对我来说重要的是图形创建就像TensorFlow,SymPy等一样简单。换句话说。我希望我可以使用多种语法来创建相同的图形。例如,它不应该很难做到:

h = h('c','d')
g = g('a','b', h )
graph = f( g, seed )

如果用户想要。

2 个答案:

答案 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
相关问题