Python - 协程

在Python中,协程是一种用于处理并发任务的轻量级线程。它们允许你在一个线程中执行多个任务,而不需要创建多个线程或进程。

概览

  1. 定义:协程是一个可以暂停和恢复的函数。你可以使用async def来定义一个协程。

  2. 异步执行:协程通过await关键字来等待其他协程的完成,而不会阻塞整个程序。这意味着你可以在等待某个操作(如网络请求)时,继续执行其他任务。

  3. 事件循环:Python使用事件循环来管理协程的执行。事件循环会调度和运行协程,确保它们在合适的时机被执行。

  4. 示例:

    import asyncio
    
    async def say_hello():
        print("Hello")
        await asyncio.sleep(1)  # 模拟异步操作
        print("World")
    
    async def main():
        await say_hello()
    
    asyncio.run(main())

在这个例子中,say_hello是一个协程,它会打印“Hello”,然后等待1秒(模拟异步操作),最后打印“World”。asyncio.run(main())启动事件循环并运行main协程。

await

await关键字用于等待另一个协程的完成或一个可等待对象(如异步I/O操作)。使用await的主要目的是让协程在等待某个操作时能够“暂停”,并允许事件循环去执行其他任务,从而提高程序的效率。

以下情况会使用await:

  1. 等待异步操作:当你需要执行一个异步操作(如网络请求、文件I/O等)并希望在操作完成之前不阻塞其他任务时,使用await。

  2. 调用其他协程:当你在一个协程中调用另一个协程时,应该使用await来确保调用的协程在完成之前不会继续执行后面的代码。

什么情况不用await:

print是一个普通的同步函数,它会立即执行并输出内容。它不涉及任何异步操作,因此不需要使用await。在协程中,只有那些返回可等待对象的操作(如asyncio.sleep()、网络请求等)才需要使用await。

示例说明:

import asyncio

async def say_hello():
    print("Hello")  # 这里不需要await,因为print是同步的
    await asyncio.sleep(1)  # 这里需要await,因为这是一个异步操作
    print("World")

async def main():
    await say_hello()

asyncio.run(main())

在这个例子中,第一次print("Hello")会立即执行,而await asyncio.sleep(1)会让协程暂停1秒,期间事件循环可以执行其他任务。最后,print("World")在等待结束后执行。

如何判断对象是否是可等待的(await)

在Python中,通常可以通过以下几种方式来判断一个函数是否返回可等待对象(即可以使用await的对象):

  1. 查看文档:大多数库和框架的文档会明确说明哪些函数是异步的,并且返回可等待对象。查阅官方文档是了解函数行为的最佳方式。

  2. 函数定义:如果一个函数是用async def定义的,那么它返回的就是一个协程对象,可以被await。例如:

    async def my_coroutine():
        pass
  3. 使用inspect模块:Python的inspect模块可以用来检查对象的类型。你可以使用inspect.iscoroutinefunction()来判断一个函数是否是协程函数,或者使用inspect.isawaitable()来判断一个对象是否是可等待的。例如:

    import inspect
    
    async def my_coroutine():
        pass
    
    print(inspect.iscoroutinefunction(my_coroutine))  # 输出: True
  4. 常见的异步库:许多流行的库(如asyncio、aiohttp、asyncpg等)提供的函数通常是异步的。你可以查看这些库的文档,了解哪些函数是异步的。

  5. 使用asyncio的isawaitable:你可以使用asyncio.isawaitable()来检查一个对象是否是可等待的。例如:

    import asyncio
    
    async def my_coroutine():
        return 42
    
    result = my_coroutine()  # 返回一个协程对象
    print(asyncio.isawaitable(result))  # 输出: True

如何理解一个程序的可等待架构

为什么Telethon用协程呢? 它使用异步编程的主要原因是为了提高性能和响应能力。我们可以想象作为一个telegram的客户端, 它可能在你上传文件的同时, 也会收到别人给你的信息提醒. 这就是异步. 如果有这样的需求, 那么用异步协程是最好的选择.