Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
259 views
in Technique[技术] by (71.8m points)

python - Why can I only use the await keyword inside of async function?

Suppose I have code like this

async def fetch_text() -> str:
    return "text "

async def show_something():
    something = await fetch_text()
    print(something)

Which is fine. But then I want to clean the data, so I do

async def fetch_text() -> str:
    return "text "

def fetch_clean_text(text: str) -> str:
    text = await fetch_text()
    return text.strip(text)

async def show_something():
    something = fetch_clean_text()
    print(something)

(I could clean text inside show_something(), but let's assume that show_something() can print many things and doesn't or shouldn't know the proper way of cleaning them.)

This is of course a SyntaxError: 'await' outside async function. But—if this code could run—while the await expression is not placed inside a coroutine function, it is executed in the context of one. Why this behavior is not allowed?

I see one pro in this design; in my latter example, you can't see that show_something()'s body is doing something that can result in its suspension. But if I were to make fetch_clean_text() a coroutine, not only would it complicate things but would probably also reduce performance. It just makes little sense to have another coroutine that doesn't perform any I/O by itself. Is there a better way?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I see one pro in this design; in my latter example, you can't see that show_something()'s body is doing something that can result in its suspension.

That's exactly why it designed this way. Writing concurrent code can be very tricky and asyncio authors decided that it's critically important to always explicitly mark places of suspend in code.

This article explains it in details (you can start from "Get To The Point Already" paragraph).

But if I were to make fetch_clean_text() a coroutine, not only would it complicate things but would probably also reduce performance.

You need coroutines almost exclusively when you deal with I/O. I/O always takes much-much more time than overhead for using coroutines. So I guess it can be said - no, comparing to I/O you already deal with, you won't lose any significant amount of execution time for using coroutines.

Is there a better way?

Only way I can suggest: is to maximally split logic that deals with I/O (async part) from rest of the code (sync part).

from typing import Awaitable

def clean_text(text: str) -> str:
    return text.strip(text)

async def fetch_text() -> Awaitable[str]:
    return "text "

async def fetch_clean_text(text: str) -> Awaitable[str]:
    text = await fetch_text()
    return clean_text(text)

async def show_something():
    something = await fetch_clean_text()
    print(something)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...