我正在使用asyncio从字典中收集任务并执行它们,但是我很难使其按预期工作。 (这是我的问题here的后续问题,但我重新编写了一下代码,因为它没有按预期工作,因此决定改用包装器功能会更好。)
所以我正在使用包装器来调用指定的任务函数。我希望包装程序将所有* args或** kwargs转发到任务函数,并且如果设置了interval
kwarg,也要定期重复执行该任务。
如何将这些信息传递给包装器和任务函数,同时又使其易于维护并能够轻松地向tasks
词典中添加新任务呢?
请看一下我的代码以作说明。
import asyncio
import random
async def run_task(taskname, taskfunc, interval=None, *args, **kwargs):
# Wrapper which will run the specified function, and repeat it if 'interval' is set.
# Should also be able to pass any potential *args and **kwargs to the function.
fakedelay = random.randint(1,6)
print(f'{taskname} started (completing in {fakedelay} seconds)')
await taskfunc(fakedelay, *args, **kwargs)
print(f'{taskname} completed after {fakedelay} seconds')
if interval is not None:
print(f'Repeating {taskname} in {interval} seconds...')
while True:
await taskfunc(fakedelay, *args, **kwargs)
await asyncio.sleep(interval)
async def faketask(fakedelay, *args, **kwargs):
# Function to simulate a coroutine task
await asyncio.sleep(fakedelay)
async def main():
tasks = {
# Dictionary of tasks to perform
'Task-1': faketask,
'Task-2': faketask,
'Task-3': faketask,
}
tasklist = []
for taskname, taskfunc in tasks.items():
tasklist.append(run_task(taskname, taskfunc))
print(f'Added {taskname} to job queue.')
await asyncio.gather(*tasklist)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
到目前为止,这似乎效果很好。但是,假设我希望Task-3在每次完成后每隔10秒重复一次。我想在tasks
字典中简单地指定它,以使其尽可能简单在将来添加新任务。例如。像这样:
tasks = {
# Dictionary of tasks to perform
'Task-1': faketask,
'Task-2': faketask,
'Task-3': faketask(interval=10),
}
但是运行它可以
TypeError: faketask() missing 1 required positional argument: 'fakedelay'
我认为这是有道理的,因为interval
kwarg用于包装程序,而不是任务功能(faketask
)本身。而且包装程序似乎无法添加任何* args或** kwargs(在这种情况下为fakedelay
)。
在上一个问题中,我被建议使用functools.partial
。
tasks = {
'Task-1': faketask,
'Task-2': faketask,
'Task-3': functools.partial(faketask, interval=10),
}
它在某种程度上解决了我上一个问题的问题,但是在重新编写代码并添加了包装函数之后,它现在似乎什么也不做,并且坦白地说,我在理解如何使用functools.partial
时遇到了困难
我的问题是
我该怎么办,这是完成我想做的事情的适当方法吗?
我如何以尽可能简单的方式(为新任务可以轻松添加)为tasks
词典中的特定函数提供* args和** kwargs通过包装转发到任务功能本身?
我的周期性重复函数的方法是否正确?我特别希望它仅在完成之后 睡觉,然后再重新开始,而不是即使最后一个实例尚未完成也不会再次触发。
答案 0 :(得分:2)
仅当您实际包装functools.partial
以包括可选的关键字参数时,才使用faketask
。如果您需要将关键字参数应用于其他函数(run_task
),则需要独立执行。例如,您可以在run_task
字典中为tasks
指定其他视蛋白:
tasks = {
'Task-1': faketask,
'Task-2': faketask,
'Task-3': (faketask, {'interval': 10)),
}
然后,调用run_task
的代码将需要识别元组:
for taskname, taskfunc_maybe_with_options in tasks.items():
if isinstance(taskfunc_maybe_with_options, tuple):
taskfunc, options = taskfunc_maybe_with_options
else:
taskfunc = taskfunc_maybe_with_options
options = {}
tasklist.append(run_task(taskname, taskfunc, **options))