我有类似于概述here的情况,除了不是用多个参数链接任务,我想链接返回带有多个条目的字典的任务。
这是 - 非常松散和抽象地 - 我想要做的事情:
tasks.py
@task()
def task1(item1=None, item2=None):
item3 = #do some stuff with item1 and item2 to yield item3
return_object = dict(item1=item1, item2=item2, item3=item3)
return return_object
def task2(item1=None, item2=None, item3=None):
item4 = #do something with item1, item2, item3 to yield item4
return_object = dict(item1=item1, item2=item2, item3=item3, item4=item4)
return return_object
从ipython开始,我可以单独和异步地调用task1,没有任何问题。
我也可以单独调用task2,将task1返回的结果作为双星参数:
>>res1 = task1.s(item1=something, item2=something_else).apply_async()
>>res1.status
'SUCCESS'
>>res2 = task2.s(**res1.result).apply_async()
>>res2.status
'SUCCESS
然而,我最终想要实现的是与上面相同的最终结果,但通过链,在这里,我无法弄清楚如何使用(位置)参数实例化task2 由task1返回,但task1.result为** kwargs:
chain_result = (task1.s(item1=something, item2=something_else) | task2.s()).apply_async() #THIS DOESN'T WORK!
我怀疑我可以回去重写我的任务,以便他们返回位置参数而不是字典,这可能会让事情变得清晰,但在我看来应该有一些方法来访问task1' s使用**双星的等效功能返回task2中的对象。我还怀疑我在这里遗漏了一些关于Celery子任务实现或* args与** kwargs相当明显的东西。
希望这是有道理的。并提前感谢任何提示。
答案 0 :(得分:7)
这是我对问题的看法,使用抽象的任务类:
from __future__ import absolute_import
from celery import Task
from myapp.tasks.celery import app
class ChainedTask(Task):
abstract = True
def __call__(self, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], dict):
kwargs.update(args[0])
args = ()
return super(ChainedTask, self).__call__(*args, **kwargs)
@app.task(base=ChainedTask)
def task1(x, y):
return {'x': x * 2, 'y': y * 2, 'z': x * y}
@app.task(base=ChainedTask)
def task2(x, y, z):
return {'x': x * 3, 'y': y * 3, 'z': z * 2}
您现在可以定义并执行您的链:
from celery import chain
pipe = chain(task1.s(x=1, y=2) | task2.s())
pipe.apply_async()
答案 1 :(得分:1)
chain
和其他画布原语属于
功能实用程序,如map
和reduce
。
E.g。其中map(target, items)
为列表中的每个项目调用target(item)
,
Python有一个很少使用的地图版本itertools.starmap
,
而是调用target(*item)
。
虽然我们可以将starchain
甚至kwstarchain
添加到工具箱中,但这些都是
会很专业,可能不经常使用。
有趣的是,Python使列表和生成器表达式不再需要这些,
以便将地图替换为[target(item) for item in item]
,将地图替换为[target(*item) for item in item]
。
因此,我认为不应该为每个原语实现几个替代方案 专注于寻找一种更灵活的支持方式,例如:比如芹菜动力发电机的表达方式(如果可能的话,如果没有同样强大的东西)
答案 2 :(得分:1)
由于这不是内置于芹菜,我自己写了一个类似于装饰器的函数。
# Use this wrapper with functions in chains that return a tuple. The
# next function in the chain will get called with that the contents of
# tuple as (first) positional args, rather than just as just the first
# arg. Note that both the sending and receiving function must have
# this wrapper, which goes between the @task decorator and the
# function definition. This wrapper should not otherwise interfere
# when these conditions are not met.
class UnwrapMe(object):
def __init__(self, contents):
self.contents = contents
def __call__(self):
return self.contents
def wrap_for_chain(f):
""" Too much deep magic. """
@functools.wraps(f)
def _wrapper(*args, **kwargs):
if type(args[0]) == UnwrapMe:
args = list(args[0]()) + list(args[1:])
result = f(*args, **kwargs)
if type(result) == tuple and current_task.request.callbacks:
return UnwrapMe(result)
else:
return result
return _wrapper
我可以像starchain
概念那样展开,但您可以轻松修改它以解开kwargs。