V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
nicegoing
V2EX  ›  Python

初学 python,大家帮忙看看这段协程代码,运行结果想了半天还没想通

  •  
  •   nicegoing · 2016-11-03 08:59:30 +08:00 · 4700 次点击
    这是一个创建于 2942 天前的主题,其中的信息可能已经有所发展或是发生改变。
    import logging; logging.basicConfig(level=logging.INFO)
    
    import asyncio, os, json, time
    from datetime import datetime
    
    from aiohttp import web
    
    def index(request):
        logging.info('server response...')
        return web.Response(body=b'<h1>Awesome</h1>')
    
    async def init(loop):
        app = web.Application(loop=loop)
        app.router.add_route('GET', '/', index)
        logging.info('server before...')
        srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000)
        logging.info('server started at http://127.0.0.1:9000...')
        return srv
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(init(loop))
    loop.run_forever()
    

    我觉得运行的日志:

    INFO:root:server before...
    INFO:root:server response...
    INFO:root:server started at http://127.0.0.1:9000...
    

    结果正确的日志是:

    INFO:root:server before...
    INFO:root:server started at http://127.0.0.1:9000...
    INFO:root:server response...
    

    在我的理解里, python 执行到 await 应该暂停执行函数, TCP 协程结束后,才执行 server started at...这条日志。怎么函数一下子全部执行完了。

    14 条回复    2016-11-03 11:03:45 +08:00
    cloverstd
        1
    cloverstd  
       2016-11-03 09:08:05 +08:00 via iPhone
    因为你的 init 函数没有用 await init 调用
    ifaii
        2
    ifaii  
       2016-11-03 09:11:30 +08:00
    async def init(loop):

    学艺不精 看不懂-_-||
    nicegoing
        3
    nicegoing  
    OP
       2016-11-03 09:17:13 +08:00
    @cloverstd https://docs.python.org/3/library/asyncio-task.html#example-chain-coroutines
    这是 python 的官方示例。这个例子调用的时候也没有 await 调用呀
    ```
    import asyncio

    async def compute(x, y):
    print("Compute %s + %s ..." % (x, y))
    await asyncio.sleep(1.0)
    return x + y

    async def print_sum(x, y):
    result = await compute(x, y)
    print("%s + %s = %s" % (x, y, result))

    loop = asyncio.get_event_loop()
    loop.run_until_complete(print_sum(1, 2))
    loop.close()
    ```
    sujin190
        4
    sujin190  
       2016-11-03 09:20:45 +08:00
    没错啊, create_server 只是 bing 了没有 listen 啊,所以立刻返回了, run_forever 才进行 listen 的行为
    nicegoing
        5
    nicegoing  
    OP
       2016-11-03 09:21:05 +08:00
    @ifaii 我也不懂,纠结中
    sujin190
        6
    sujin190  
       2016-11-03 09:21:58 +08:00
    没错啊, create_server 只是 bind 了没有 listen 啊,所以立刻返回了, run_forever 才进行 listen 的行为
    cloverstd
        7
    cloverstd  
       2016-11-03 09:29:53 +08:00
    @nicegoing 你的 init 返回的是一个 future ,直接 init() 会当做普通函数来执行
    ruoyu0088
        8
    ruoyu0088  
       2016-11-03 09:30:55 +08:00
    loop.create_server 是创建一个服务器对象, await loop.create_server(...)是等待创建这个服务器对象,并不是等待这个服务器响应请求。
    yufpga
        9
    yufpga  
       2016-11-03 09:36:57 +08:00
    await 的语义相当于 yield from(首先你得先搞清楚这个东西), 不要和 yield 这个鬼搞混淆了, 出现 async 和 await 支持的原因本就是为了使用写同步代码的方式写出异步的效果.在你这里, 正常逻辑考虑, 你的 server 都还没有创建, 怎么可能请求成功, 从而执行 index 函数.建议 debug 追踪下代码内部的执行逻辑,要熟悉其内部原理, 最好看一下 asyncio 的源码, 自己实现一下
    nicegoing
        10
    nicegoing  
    OP
       2016-11-03 09:37:30 +08:00
    @cloverstd
    @sujin190
    @ruoyu0088
    谢谢大家,有点儿明白了。
    nicegoing
        11
    nicegoing  
    OP
       2016-11-03 09:40:40 +08:00
    @yufpga 感觉 yield from 和 yield 没什么大区别。谢谢,我去读读源码
    yufpga
        12
    yufpga  
       2016-11-03 09:57:13 +08:00
    @sujin190 你的说法是有问题的, run_forever 并不是进行 server 的 listen 的行为,而是执行了一段类似与下面的代码
    while not self.stopped:
    events = selector.select(self.select_timeout)
    if not events:
    raise Exception('轮询超时')
    for event_key, event_mask in events:
    callback = event_key.data
    callback(event_key, event_mask)
    用来取出可读可写等事件, 其实就是通过事件循环驱动(通知)阻塞程序恢复执行
    sujin190
        13
    sujin190  
       2016-11-03 10:50:18 +08:00
    @yufpga listen 做的事情也是这样的啊,有什么区别么
    lytofb
        14
    lytofb  
       2016-11-03 11:03:45 +08:00
    没什么问题啊, win7 64 位 python 3.5.2
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   992 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 20:02 · PVG 04:02 · LAX 12:02 · JFK 15:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.