我正在尝试设置一个系统来生成一组“配置”。 这些配置是存储在python dict中的简单键/值对。
这些配置是使用一系列函数转换dict的结果,这就是我所说的工作流程。
以下是我最终得到的一个简单示例:
global_data = [dict()]
def workflow_step1(data):
results = []
for i in range(1,4):
data['key'] = i
results.append(copy.deepcopy(data))
return results
def workflow_step2(data):
results = []
for i in range(1,3):
data['otherkey'] = i
results.append(copy.deepcopy(data))
return results
def workflow_step3(data):
data['yetanotherkey'] = 42
return [copy.deepcopy(data)]
def list_workflow():
return [workflow_step1, workflow_step2, workflow_step3]
def merge(lhs,rhs):
return lhs+rhs
def run(data):
for step in list_workflow():
data = reduce(lambda lhs, rhs: lhs+rhs, [step(d) for d in data])
return data
print run(global_data)
这样做很好,我得到:
[{'yetanotherkey': 42, 'otherkey': 1, 'key': 1},
{'yetanotherkey': 42, 'otherkey': 2, 'key': 1},
{'yetanotherkey': 42, 'otherkey': 1, 'key': 2},
{'yetanotherkey': 42, 'otherkey': 2, 'key': 2},
{'yetanotherkey': 42, 'otherkey': 1, 'key': 3},
{'yetanotherkey': 42, 'otherkey': 2, 'key': 3}]
如您所见,目标是获得所有可能的dict组合。工作流程的每个步骤都会返回一组可能的组合,这些组合应该为即将到来的步骤创建新的可能性分支。
我面临的问题是,用户现在正在创建越来越多的工作流程步骤,从而导致组合爆炸。
我天真的设计中存在的问题是,我会立刻生成所有可能性的整个树。
我希望使用yield
和生成器解决这个问题,一次生成一种可能性,因此不能同时存储所有内容。
我当然能够使用yield:
重写工作流程步骤def workflow_step1(data):
for i in range(1,4):
data['key'] = i
yield copy.deepcopy(data)
def workflow_step2(data):
for i in range(1,3):
data['otherkey'] = i
yield copy.deepcopy(data)
def workflow_step3(data):
data['yetanotherkey'] = 42
yield copy.deepcopy(data)
def list_workflow():
yield workflow_step1
yield workflow_step2
yield workflow_step3
但我无法让我的大脑想到重写run
函数以顺序处理每个步骤的方法。我迷失在收益率和发电机的脑力迷中。
任何想法都更受欢迎!
答案 0 :(得分:3)
我认为itertools.product
会完全符合您的要求。这是一种返回生成器的方法,该生成器一次生成三个步骤的一个组合。即使一步有更多的选项,也不会占用大量的时间或内存。
def step1():
return [("key", i) for i in range(1,4)]
def step2():
return [("otherkey", i) for i in range(1,3)]
def step3():
return [("yetanotherkey", 42)]
def workflow_generator():
return (dict(p) for p in itertools.product(step1(), step2(), step3()))
如果您希望能够处理可变数量的步骤,您可以稍微修改一下以使其工作:
def workflow_generator(steps):
return (dict(p) for p in itertools.product(*(step() for step in steps)))
使用workflow_generator([step1, step2, step3])
调用此版本将得到与先前版本相同的结果,但如果需要(例如来自函数),您可以以其他方式组装参数。
答案 1 :(得分:0)
是的,您的数据结构搞砸了。以下代码仅用于提供一个想法(在当前结构方面不完全可操作)。你应该基本上使用树,并创建一个像注册步骤的工作流管理器一样的类。步骤是步骤树。使用真实的id而不是数字。
两个建议
1
import copy
global_data = [dict()]
class workflowManager:
def __init__(self):
self.steps = []
self.data = list()
def registerStep(self,step,stepNumber=1):
for i in range(1,stepNumber+1):
self.steps.append(step)
def registerSubStep(self,step,substep):
'''
def hookToStep(self,step,hook):
#find all steps
indices = [i for i, x in enumerate(self.steps) if x == step]
print 'hooking at ',indices
for k in indices:
a = self.steps[:k]
b = self.steps[k:]
self.steps = a + [hook] + b
'''
def performOnData(self):
print 'self.data ',self.data
for step in self.steps:
print 'performing step ',step
print 'data ',self.data
self.data = step(self.data)
def __str(self):
return str(data)
def step1(data):
lastn = 0
try:
lastn = data[-1]['key']
except:
pass
data.append({'key': lastn+1})
return data
def step2(data):
lastn = 0
try:
lastn = data[-1]['otherkey']
except:
pass
data.append({'otherkey': lastn+1})
return data
def step3(data):
data.append({'yetanotherkey': 42})
return data
w = workflowManager()
w.step_register(step1,4)
#w.step_register(step2,3)
#w.step_register(step3,1)
w.hookToStep(step1,step3)
print w
w.performOnData()
print w
2
class Step:
def __init__(self,name,extra=None):
self.steps = []
self.name = name
def addChild(self,child,repeat=1):
for j in range(1,repeat+1):
self.steps.append(child)
def __str__(self):
s = self.name + "\n"
for sub in self.steps:
s+=str(sub)
return s
step1 = Step("yetanotherkey",42) #root
step2 = Step("otherkey")
step3 = Step("key")
step2.addChild(step3,2)
step1.addChild(step2,3)
print step1
答案 2 :(得分:0)
我建议你从workflow_step
函数中取出循环,并像这样使用itertools.product
:
import copy
import itertools
def workflow_step1(data, param):
data['key'] = param
def workflow_step2(data, param):
data['otherkey'] = param
def workflow_step3(data, param):
data['yetanotherkey'] = param
def list_workflow():
return ([workflow_step1, workflow_step2, workflow_step3],
[range(1,4), range(1,3), [42]])
def run(data):
steps, param_lists = list_workflow()
for params in itertools.product(*param_lists):
d = copy.deepcopy(data)
for step, param in zip(steps, params):
step(d,param)
yield d
for result in run({}):
print result