在asyncio中链接协程(和观察者模式)

gnr*_*gnr 9 python python-3.x python-asyncio

我无法将协同程序链接在一起.在一个比hello world或factorial稍微不那么简单的例子中,我想要一个循环来持续监视文件修改时间,然后在触摸文件时打印出时间:

#!/usr/bin/env python3
import os
import asyncio

@asyncio.coroutine
def pathmonitor(path):
    modtime = os.path.getmtime(path)
    while True:
        new_time = os.path.getmtime(path)
        if new_time != modtime:
            modtime = new_time
            yield modtime
        yield from asyncio.sleep(1)

@asyncio.coroutine
def printer():
    while True:
        modtime = yield from pathmonitor('/home/users/gnr/tempfile')
        print(modtime)

loop = asyncio.get_event_loop()
loop.run_until_complete(printer())
loop.run_forever()
Run Code Online (Sandbox Code Playgroud)

我希望这可以工作 - 但是,当我运行它时,我得到一个:

RuntimeError: Task got bad yield: 1426449327.2590399
Run Code Online (Sandbox Code Playgroud)

我在这做错了什么?

更新:请参阅下面的答案,了解观察者模式的示例(即,在文件被触摸时有效地允许多个注册者获得更新),而不使用回调(您必须使用任务).

UPDATE2:有一个更好的解决方案:3.5 async for(异步迭代器):https://www.python.org/dev/peps/pep-0492/

And*_*zlo 5

我通过使用return而不是yield在链式协程中使用您的代码,就像链式协程示例一样

#!/usr/bin/env python3
import os
import asyncio2

@asyncio.coroutine
def pathmonitor(path):
    modtime = os.path.getmtime(path)
    while True:
        new_time = os.path.getmtime(path)
        if new_time != modtime:
            modtime = new_time
            return modtime
        yield from asyncio.sleep(1)


@asyncio.coroutine
def printer():
    while True:
        modtime = yield from pathmonitor('/tmp/foo.txt')
        print(modtime)


loop = asyncio.get_event_loop()
loop.run_until_complete(printer())
loop.run_forever()
Run Code Online (Sandbox Code Playgroud)

请注意,printer()的循环将为pathmonitor每次迭代创建一个新的生成器。不确定这是否是您的想法,但这可能是一个开始。

我发现协程 API 和语法让我自己有点困惑。以下是我发现有帮助的一些阅读材料:

  • @gnr 你不希望 `pathmonitor` 成为一个生成器;协程和生成器不是一回事,不能互换。虽然生成器可以同时使用 `yield`(正常情况)或 `yield from`(如果它需要从子生成器中产生所有值),但 `asyncio` 协程仅被设计为使用 `yield from`,因为 `yield from` ` 可用于从子生成器中获取实际*返回*的值。`yield subgenerator()` 调用将始终返回一个生成器对象。 (4认同)