在不导入asyncio包的情况下启动异步功能 [英] Start async function without importing the asyncio package

查看:45
本文介绍了在不导入asyncio包的情况下启动异步功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

可以启动这样的功能

async def foo():
    while True:
        print("Hello!")

是否不导入asyncio程序包(并获取事件循环)?

without importing the asyncio package (and getting the event loop)?

我正在寻找一种类似于Go的goroutine的原理,在该原理中,仅需go语句就可以启动协程.

I am looking for a principle similar to Go's goroutines, where one can launch a coroutine with only go statement.

之所以不导入asyncio软件包,仅是因为我认为应该可以在没有事件循环(明确)的情况下启动协程.我不明白为什么 async def 和类似的语句是核心语言(甚至是语法的一部分)的一部分,而启动创建的协程的方法仅通过包提供.

The reason why I'm not importing the asyncio package is simply because I think it should be possible to launch coroutine without event loop (explicit). I don't understand why async def and similar statements are part of core language (even part of syntax) and the way to launch created coroutines is available only through package.

推荐答案

当然可以在不显式使用asyncio的情况下启动async函数.毕竟,asyncio是用Python编写的,所以它也可以做得到(尽管有时您可能需要其他模块,例如selectorsthreading,如果您打算同时等待外部事件,或者并行执行一些事件,其他代码).

Of course it is possible to start an async function without explicitly using asyncio. After all, asyncio is written in Python, so all it does, you can do too (though sometimes you might need other modules like selectors or threading if you intend to concurrently wait for external events, or paralelly execute some other code).

在这种情况下,由于您的函数内部没有await点,因此只需执行一次推入即可.您通过将None插入None来推送协程.

In this case, since your function has no await points inside, it just needs a single push to get going. You push a coroutine by sending None into it.

>>> foo().send(None)
Hello!
Hello!
...

当然,如果您的函数(协程)内部包含yield表达式,它将在每个yield点暂停执行,并且您需要向其中推送其他值(通过coro.send(value)next(gen)) -但是您已经知道,如果您知道发电机的工作原理.

Of course, if your function (coroutine) had yield expressions inside, it would suspend execution at each yield point, and you would need to push additional values into it (by coro.send(value) or next(gen)) - but you already know that if you know how generators work.

import types

@types.coroutine
def bar():
    to_print = yield 'What should I print?'
    print('Result is', to_print)
    to_return = yield 'And what should I return?'
    return to_return

>>> b = bar()
>>> next(b)
'What should I print?'
>>> b.send('Whatever you want')
Result is Whatever you want
'And what should I return?'
>>> b.send(85)
Traceback...
StopIteration: 85

现在,如果您的函数内部包含await个表达式,它将暂停对每个表达式的求值.

Now, if your function had await expressions inside, it would suspend at evaluating each of them.

async def baz():
    first_bar, second_bar = bar(), bar()
    print('Sum of two bars is', await first_bar + await second_bar)
    return 'nothing important'

>>> t = baz()
>>> t.send(None)
'What should I print?'
>>> t.send('something')
Result is something
'And what should I return?'
>>> t.send(35)
'What should I print?'
>>> t.send('something else')
Result is something else
'And what should I return?'
>>> t.send(21)
Sum of two bars is 56
Traceback...
StopIteration: nothing important

现在,所有这些.send都开始变得乏味.半自动生成它们会很好.

Now, all these .sends are starting to get tedious. It would be nice to have them semiautomatically generated.

import random, string

def run_until_complete(t):
    prompt = t.send(None)
    try:
        while True:
            if prompt == 'What should I print?':
                prompt = t.send(random.choice(string.ascii_uppercase))
            elif prompt == 'And what should I return?':
                prompt = t.send(random.randint(10, 50))
            else:
                raise ValueError(prompt)
    except StopIteration as exc:
        print(t.__name__, 'returned', exc.value)
        t.close()

>>> run_until_complete(baz())
Result is B
Result is M
Sum of two bars is 56
baz returned nothing important

恭喜,您刚刚编写了第一个事件循环! (您没想到会发生这种情况,对吗?;)当然,这是非常原始的:它只知道如何处理两种类型的提示,它没有使t产生与之同时运行的其他协程. ,并使用random生成器伪造事件.

Congratulations, you just wrote your first event loop! (Didn't expect it to happen, did you?;) Of course, it is horribly primitive: it only knows how to handle two types of prompts, it doesn't enable t to spawn additional coroutines that run concurrently with it, and it fakes events by a random generator.

(实际上,如果您想了解哲学的话: 也可以称为事件循环:Python REPL正在将提示打印到控制台窗口,并且它依赖于您可以通过在其中输入t.send(whatever)来提供事件.:)

(In fact, if you want to get philosophical: what we did above that manually, could also be called an event loop: Python REPL was printing prompts to a console window, and it was relying on you to provide events by typing t.send(whatever) into it.:)

asyncio只是上述内容的一个广泛泛化的变体:提示由Future代替,多个协程保留在队列中,因此每个协程最终都轮流使用,事件更加丰富,包括网络/套接字通信,文件系统读/写,信号处理,线程/进程侧执行等.但是基本思想仍然是相同的:您抓取一些协程,将它们从空中传送到另一个空中,直到它们全部举起StopIteration.当所有协程无关时,您将前往外部世界并获取一些其他事件,让它们继续咀嚼,然后继续.

asyncio is just an immensely generalized variant of the above: prompts are replaced by Futures, multiple coroutines are kept in queues so each of them eventually gets its turn, and events are much richer and include network/socket communication, filesystem reads/writes, signal handling, thread/process side-execution, and so on. But the basic idea is still the same: you grab some coroutines, juggle them in the air routing the Futures from one to another, until they all raise StopIteration. When all coroutines have nothing to do, you go to external world and grab some additional events for them to chew on, and continue.

我希望现在一切都清楚了. :-)

I hope it's all much clearer now. :-)

这篇关于在不导入asyncio包的情况下启动异步功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆