Python技术迷

Python的协程函数有哪些?

协程这玩意儿,说白了就是“能暂停的函数”。

它不是你打个 def 然后 call 一下就走到底了,它可以“等一等”,然后接着来——就像你在点外卖,点完之后不干等着,而是干点别的,等外卖送到再回来继续吃饭。协程就差不多这个逻辑。

你可能早就听说过 async/await,但如果你还没真刀实枪写过协程,那今天就来好好掰扯掰扯

什么是协程函数,它到底怎么用,和普通函数有啥不一样,以及面试中要是被问到该怎么怼回去(不是,是怎么优雅回答)。

什么是协程函数?

在Python里,协程函数是用 async def 定义的。也就是说,只要你在函数前面加上 async,这个函数就变了,不再是普通的函数,而是一个“可以被暂停”的异步协程函数了。

来看个最基础的例子:

import asyncio

asyncdefsay_hello():
await asyncio.sleep(1)
    print("Hello")

asyncio.run(say_hello())

这段代码其实比你想象的还高级:

  • async def 是声明协程的标志,Python 看到这个,就知道你写的是“异步协程函数”;
  • await 是个暂停点,它告诉解释器:“我这里等一会,先去干别的”;
  • asyncio.sleep(1) 就是一个异步操作,模拟的是“我等1秒再继续干”。

注意:你不能直接 say_hello() 就让它跑起来,因为它返回的是个协程对象,不是真正的执行结果。必须通过 asyncio.run() 或事件循环来运行它。

协程和普通函数到底差在哪?

举个例子你就明白了:

defnormal_func():
    print("Start")
    time.sleep(1)
    print("End")

asyncdefcoroutine_func():
    print("Start")
await asyncio.sleep(1)
    print("End")

这俩函数干的事儿差不多,但区别在于:

  • normal_func 会阻塞主线程,睡觉的时候整个程序都得等;
  • coroutine_func 里的 await asyncio.sleep(1) 是非阻塞的,Python可以“切走”,去干别的,再回来接着干这个。

这也是协程的核心价值:高并发、低资源消耗。

在爬虫、IO密集型应用、Web服务器中,这种能力简直不要太重要。

比如你要请求1000个页面,用线程开销太大,而协程可以1个线程干1000个活儿,谁用谁知道。

那协程函数到底有哪些类型?

这个问题问得有点宽泛,其实协程函数的种类不多,但协程相关的机制挺多,咱们拆开讲:

1. async def 定义的协程函数

这类是最基本的协程函数类型,一般这样写:

asyncdeffetch_data():
await asyncio.sleep(1)
return"data"

只要你用了 async def,它返回的就不是一个函数结果,而是一个“协程对象”:

coro = fetch_data()
print(type(coro))  # <class 'coroutine'>

这个对象需要放到事件循环里去跑,比如:

result = asyncio.run(fetch_data())
print(result)

2. asyncio自带的协程方法

像 asyncio.sleep()、asyncio.gather()、asyncio.create_task() 这些,都是协程玩法里的“原厂配件”。

比如你想并发执行多个任务:

asyncdefmain():
    task1 = asyncio.create_task(fetch_data())
    task2 = asyncio.create_task(fetch_data())
await task1
await task2

asyncio.run(main())

它们会并发跑,而不是一个等一个执行完。

那生成器、yield 那些是不是协程?

这个是个常见的误区:yield 和协程,看着有点像,但本质不一样。

简单说:

  • yield 是生成器,用于“惰性生成数据”,比如你写个100万行的文件不想一次读完,用 yield 就很香;
  • async def + await 是异步协程,能实现非阻塞逻辑。

Python 3.5之后才有 async/await 的正式语法,在此之前是用 @asyncio.coroutine 和 yield from 模拟异步行为,但现在几乎没人这么写了,过时了属于是。

协程用起来要注意的那些坑

写协程,不能只学写法,还得知道雷区在哪里。

await 只能用在 async 函数里

别想着在普通函数里 await:

defbad_func():
await asyncio.sleep(1)  # 报错,SyntaxError

只能在 async def 里用 await。

协程对象得运行,不运行就啥也不干

asyncdeff():
    print("Running...")

f()  # 这啥也不会输出

你要么 await f(),要么 asyncio.run(f()),它才会真的执行。

协程和多线程哪个更好?

啊这……这题我熟!

我觉得这个问题可以这样回答:

协程和多线程本质是两种并发模型,协程是用户态的,线程是系统级别的。 对于IO密集型任务,比如网络请求、数据库访问,协程通常比线程效率更高,且更节省资源。 但对于CPU密集型任务,协程就帮不上啥忙了,还得靠多线程或多进程配合。 Python因为GIL的存在,多线程并不是真正并行,而协程是“单线程并发”,更适合应对大量IO操作。

然后面试官一般就点点头,往下翻简历了。

那协程能替代多线程吗?

不能完全替代。

协程非常适合高并发的IO操作,比如做一个高并发的接口转发系统、批量抓取网页等等,它能让你的程序像变魔术一样在几百个任务之间切换,还不怎么消耗内存。

但如果你真的是在跑一些数学密集型的、图像处理类的任务,协程就有点拉了,还是得多进程多线程顶上。

怎么理解 Python 的协程?有哪些场景?

这是高频题,一定要练熟!

标准答案我给你整理如下,放心抄,但理解最重要:

答:

协程是 Python 中用于实现并发的一种方式,它基于事件循环,通过 async def 定义,await 进行挂起和恢复。

协程最大的优势是避免线程开销,在单线程中实现高并发,尤其适合 IO 密集型任务,比如网络请求、爬虫、接口转发等。

使用协程时,必须配合 asyncio 模块,常用的写法包括 asyncio.run(), await, asyncio.gather() 等。

与多线程不同,协程是非抢占式调度,也就是任务执行时不会被打断,只有在遇到 await 时才会让出控制权,因此调度更加可控,资源消耗更低。

但协程并不能提升 CPU 密集型任务的效率,遇到此类任务仍需考虑多进程或使用 C 扩展等手段。

我觉得协程写得顺手之后,简直就是代码里的魔法棒,效率高到飞起,还写得清清爽爽,谁用谁知道。

你说现在面试不问协程的?那真是你面试的公司还没整明白asyncio怎么香。

对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
🔥虎哥私藏精品 热门推荐🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。

资料包含了《IDEA视频教程》、《最全python面试题库》、《最全项目实战源码及视频》及《毕业设计系统源码》,总量高达650GB,全部免费领取。