我正在尝试修改此@Siddharth Rout上的代码库。该项目旨在创建一个系统,该系统使用函数装饰器将函数添加到有向无环图中,以表示要在某些给定输入上执行的任务管道。
我想通过创建一个Task
类来修改项目,该类具有方法process
,该方法被修饰为可以在管道中执行任务,而不是像当前那样使用顶层函数功能。
class Task:
def __init__(self, name):
self.name = name
#@Pipeline.task()
def process(self, input):
return input
class AdditionTask(Task):
def __init__(self, name, value):
super().__init__(self, name)
self.value = value
@Pipeline.task()
def process(self, input):
return map(lambda x: x + self.value, input)
在Github repository中提供了描述Python装饰器的example3.py
代码的前提下,我认为这是有效的装饰。尽管在尝试执行此操作时似乎出现了错误,但是在提供装饰器时却产生了错误
TypeError:task()缺少1个必需的位置参数:“ self”
据我所知,何时实例化AdditionTask
时,函数装饰器与Pipeline()
对象的实例化不在同一范围内。如下面的示例SSCCE所证明的
from collections import deque
# https://github.com/vdymna/generic-python-pipeline/blob/master/pipeline/dag.py
class DAG:
"""Directed acyclic graph structure to manage pipeline task dependecies."""
def __init__(self):
self.graph = {}
def add(self, node, points_to=None):
"""Add new task not to the graph, specify optional 'points_to' parameter."""
if node not in self.graph:
self.graph[node] = []
if points_to:
if points_to not in self.graph:
self.graph[points_to] = []
self.graph[node].append(points_to) # todo: need to make sure not to add duplicates
# if sorted tasks and original graph lengths there must be a cycle
if len(self.sort()) != len(self.graph):
raise Exception('A cycle is detected in the graph')
def sort(self):
"""Sort all the task nodes based on the dependencies."""
self.in_degrees()
nodes_to_visit = deque()
for node, pointers in self.degrees.items():
# find all root nodes
if pointers == 0:
nodes_to_visit.append(node)
sorted_nodes = []
while nodes_to_visit:
node = nodes_to_visit.popleft()
for pointer in self.graph[node]:
self.degrees[pointer] -= 1
if self.degrees[pointer] == 0:
nodes_to_visit.append(pointer)
sorted_nodes.append(node)
return sorted_nodes
def in_degrees(self):
"""Determing number of in-coming edges for each task node."""
self.degrees = {}
for node in self.graph:
if node not in self.degrees:
self.degrees[node] = 0
for pointed in self.graph[node]:
if pointed not in self.degrees:
self.degrees[pointed] = 0
self.degrees[pointed] += 1
# https://github.com/vdymna/generic-python-pipeline/blob/master/pipeline/pipeline.py
class Pipeline:
"""Create a pipeline by chaining multiple tasks and identifying dependencies."""
def __init__(self):
self.tasks = DAG()
def task(self, depends_on=None):
"""Add new task to the pipeline and specify dependency task (optional)."""
def inner(func):
if depends_on:
self.tasks.add(depends_on, func)
else:
self.tasks.add(func)
return func
return inner
def run(self, *args):
"""Execute the pipeline and return each task results."""
sorted_tasks = self.tasks.sort()
completed = {}
for task in sorted_tasks:
for depend_on, nodes in self.tasks.graph.items():
if task in nodes:
completed[task] = task(completed[depend_on])
if task not in completed:
if sorted_tasks.index(task) == 0:
completed[task] = task(*args)
else:
completed[task] = task()
return completed
class Task:
def __init__(self, name):
self.name = name
#@Pipeline.task()
def process(self, input):
return input
class AdditionTask(Task):
def __init__(self, name, value):
super().__init__(self, name)
self.value = value
@Pipeline.task()
def process(self, input):
return map(lambda x: x + self.value, input)
if __name__ == "__main__":
pipeline = Pipeline()
add_op = AdditionTask(4)
print(pipeline.run(range(0, 4)))
是否可以使用Pipeline
中定义的函数装饰器来装饰类函数,以将类函数包装到管道功能中?
我现在唯一可能的解决方案是在Pipeline
中创建一个全局类,也就是说,我不用self.tasks
来定义Pipeline.Tasks
,而是使用{{到达管道的1}} s与范围无关。但是,如果我要创建多个Task
以及在管道任务和DAG的管理中,就会遇到问题。
编辑::嗯,我想我想要类似于最终的高级装饰器示例this link的东西。在这里,将创建一个装饰器,该装饰器将应用于类。装饰器检查装饰的类的属性,然后将其应用于另一个装饰器。在这种情况下,装饰器确定函数的运行时。在帖子中,
Pipeline
尽管这仍然再次出现我在原始发布文本中标识的相同问题,但是我看不到将def time_this(original_function):
print "decorating"
def new_function(*args,**kwargs):
print "starting timer"
import datetime
before = datetime.datetime.now()
x = original_function(*args,**kwargs)
after = datetime.datetime.now()
print "Elapsed Time = {0}".format(after-before)
return x
return new_function
def time_all_class_methods(Cls):
class NewCls(object):
def __init__(self,*args,**kwargs):
self.oInstance = Cls(*args,**kwargs)
def __getattribute__(self,s):
"""
this is called whenever any attribute of a NewCls object is accessed. This function first tries to
get the attribute off NewCls. If it fails then it tries to fetch the attribute from self.oInstance (an
instance of the decorated class). If it manages to fetch the attribute from self.oInstance, and
the attribute is an instance method then `time_this` is applied.
"""
try:
x = super(NewCls,self).__getattribute__(s)
except AttributeError:
pass
else:
return x
x = self.oInstance.__getattribute__(s)
if type(x) == type(self.__init__): # it is an instance method
return time_this(x) # this is equivalent of just decorating the method with time_this
else:
return x
return NewCls
#now lets make a dummy class to test it out on:
@time_all_class_methods
class Foo(object):
def a(self):
print "entering a"
import time
time.sleep(3)
print "exiting a"
oF = Foo()
oF.a()
实例传递给Pipeline
描述的装饰器的方式。尽管我可以用类似于time_all_class_methods
的类来装饰Task
类,但是它仍然不知道time_all_class_methods
的给定实例化实例将被添加装饰的属性;到Pipeline
。