迭代器和生成器
迭代器
概念上: 迭代器可以用来表示一个数据流, 提供了数据的惰性返回功能(只有我们主动去使用next方法调用, 才会返回值).
实现上: 实现了
__next__
接口的对象传统声明一个列表, 里面的元素会立即写进内存当中, 占用大量内存.
迭代器可以一次只返回一个元素, 占用内存非常小, 在读取大文件和大的数据集合的时候特别有用
通过
iter
方法返回一个迭代器对象# 两者实现的功能是一摸一样的 l = list(range(10**7)) l2 = iter(range(10**7))
通过
next
方法主动获取迭代器中的值# 当迭代器中没有值了以后, 会抛出StopIteration的异常, 需要大家自行处理一下 l = iter(range(5)) print(next(l)) print(next(l)) print(next(l)) print(next(l)) print(next(l)) print(next(l))
生成器
生成器是一种特殊的迭代器, 在迭代器惰性返回数据的基础上, 提供了额外的功能, 实现了程序的暂停.
声明一个生成器
只要函数体中有
yield
关键词, 它就是一个生成器yield翻译为让渡, 我们可以简单理解为暂停并返回右边的值
def my_range_gen(n): for i in range(n): yield i*i print(f"current index: {i}") my_range = my_range_gen(10) print(my_range) print(next(my_range)) print(next(my_range)) print(next(my_range)) print(next(my_range))
生成器和迭代器的区别?
同样提供了惰性返回的功能, 迭代器侧重于提供数据的惰性返回功能, 生成器侧重于指令的惰性返回功能
协程
协程的原理
协程的实现原理就是生成器的实现原理, 在生成器的基础上又提供了传递值的功能.
通过
send
方法向生成器传递值, 以下例子中, b就是通过send方法赋值为2对生成器进行
send
操作一定要调用next
方法预激, 使其停留在第一个yield位置def simple_coro(a): print("初始值 a=", a) b = yield a print("传递值 b=", b) c = yield a + b print("传递值 c=", c) coro = simple_coro(1) print(next(coro)) print(coro.send(2)) print(coro.send(3))
用协程实现计算平均数的函数
def coro_avg(): total = 0 length = 0 while True: try: value = yield total/length except ZeroDivisionError: value = yield 0 total += value length += 1 my_avg = coro_avg() print(next(my_avg)) print(my_avg.send(2)) print(my_avg.send(3))
yield
和yield from
yield from实现的协程异步程序晦涩难懂, 在python3.4引用asyncio标准库之后被弃用
yield from
用来驱动子程序中的循环并返回最终值def return_triple(): while True: value = yield if value % 3 == 0: return value def triple_recorder(): while True: result = yield from return_triple() triple_array.append(result) triple_array = [] coro = triple_recorder() next(coro) for i in range(100): coro.send(i) print(triple_array)
异步I/O
asyncio(异步)
Python3.4引入的标准库, 替换yield from实现协程异步IO, 可以更好地实现异步程序
实现原理: 自动维护了一个事件队列, 然后循环访问事件来完成异步的消息维护.
import asyncio import time class Response: staus_code = 200 async def sim_request(index): print(f"模拟发送请求 Index: {index}") response = Response() # 模拟网络延迟 # 当前是单线程运行的, 如果调用的是time.sleep(1), 那么这个线程会被阻塞 # 当前线程被阻塞之后, 不会让渡cpu资源, 异步的效率就不会体现 await asyncio.sleep(1) print(f"request index {index}, response status_code: {response.staus_code}") return response.staus_code # 获取消息队列 loop = asyncio.get_event_loop() # 包装任务 task_array = [] for i in range(100): task_array.append(sim_request(i)) # 循环访问事件来完成异步的消息维护 loop.run_until_complete(asyncio.wait(task_array)) # 关闭事件循环 loop.close()
当前异步实际上有没有提高效率, 也关乎到你调用的第三方是不是异步的.
这也是当前python异步的一个痛点, 就是丰富的第三方库不是都支持asyncio的.
小技巧: 获取异步完成之后的所有返回值
result = loop.run_until_complete(asyncio.gather(*task_array)) print(result)
Comment here is closed