面试题答案
一键面试Python生成器底层实现机制
- 状态机模型
- 生成器本质上是一种状态机。当函数中包含
yield
关键字时,该函数就成为一个生成器函数。调用生成器函数并不会立即执行函数体,而是返回一个生成器对象。 - 生成器对象在执行过程中,每次遇到
yield
语句时,会暂停函数的执行,并保存当前函数的执行状态(包括局部变量、指令指针等)。下次通过next()
函数或在for
循环等迭代环境中继续调用时,会从暂停的位置恢复执行。 - 例如:
这里生成器def simple_generator(): yield 1 yield 2 gen = simple_generator() print(next(gen)) # 输出1 print(next(gen)) # 输出2
gen
在执行yield 1
后暂停,下次next(gen)
从yield 1
之后的位置开始执行。 - 生成器本质上是一种状态机。当函数中包含
Python协程底层实现机制
- 调度原理
- Python的协程主要基于
asyncio
库。协程通过async
和await
关键字来定义和使用。 - 协程本质上也是一种特殊的生成器(
async def
定义的协程函数返回的对象是coroutine
对象,而types.coroutine
装饰的生成器函数也可以当作协程使用)。 - 调度器(
event loop
)是asyncio
实现协程调度的核心。事件循环不断地检查协程队列,当一个协程执行到await
语句时,它会暂停并将控制权交回给事件循环。事件循环会调度其他可运行的协程,当await
的对象(通常是一个Future
对象)完成时,该协程会被重新放入可运行队列,等待事件循环再次调度执行。 - 例如:
在这个例子中,import asyncio async def coroutine1(): print('Coroutine 1 start') await asyncio.sleep(1) print('Coroutine 1 end') async def coroutine2(): print('Coroutine 2 start') await asyncio.sleep(1) print('Coroutine 2 end') async def main(): await asyncio.gather(coroutine1(), coroutine2()) if __name__ == '__main__': asyncio.run(main())
coroutine1
和coroutine2
在await asyncio.sleep(1)
处暂停,事件循环调度其他协程(这里没有其他更多协程,但原理如此),1秒后协程继续执行。 - Python的协程主要基于
高并发场景下生成器和协程代码性能优化思路及技术手段
- 生成器优化思路
- 减少生成器内的计算开销:尽量避免在生成器函数内部进行复杂、耗时的计算。如果有必要的计算,可以考虑将其移到生成器外部,在生成器中只进行简单的逻辑判断和数据准备。例如,如果生成器要生成大量经过复杂计算的数据,可以先批量计算好数据,再通过生成器逐步返回。
- 合理使用生成器表达式:相比于定义一个完整的生成器函数,在一些简单场景下,使用生成器表达式可以更简洁,并且在性能上也有一定优势。例如,
(i for i in range(1000))
比定义一个生成器函数来生成同样的数据更简洁高效。
- 协程优化思路
- 减少I/O等待时间:在协程中,I/O操作(如网络请求、文件读写等)通常是性能瓶颈。可以通过优化I/O操作来提高性能。例如,使用更高效的网络库(如
aiohttp
代替requests
进行异步网络请求),或者优化文件读写的方式,尽量减少I/O操作的次数。 - 优化协程调度:合理调整协程的数量,避免过多协程导致调度开销过大。可以通过分析业务场景,确定一个合适的协程并发数。例如,对于一些对CPU计算有一定要求的协程任务,可以适当减少并发数,防止CPU资源过度竞争。
- 使用缓存:如果协程中存在重复的计算或I/O操作,可以使用缓存来避免重复工作。例如,使用
functools.lru_cache
装饰器对一些计算函数进行缓存,或者在协程中使用内存缓存(如aiocache
库)来缓存网络请求结果等。
- 减少I/O等待时间:在协程中,I/O操作(如网络请求、文件读写等)通常是性能瓶颈。可以通过优化I/O操作来提高性能。例如,使用更高效的网络库(如
- 通用技术手段
- 性能分析工具:使用
cProfile
对生成器或协程代码进行性能分析,找出性能瓶颈所在。例如,对于生成器,可以分析生成器函数内各个语句的执行时间;对于协程,可以分析不同协程函数以及I/O操作的耗时情况。 - 异步框架优化:对于基于
asyncio
的协程代码,可以根据实际业务场景选择合适的异步框架扩展,如uvloop
代替默认的asyncio
事件循环,uvloop
是一个高性能的事件循环实现,能显著提升协程的执行效率。
- 性能分析工具:使用