Python 生成器与迭代器:用惰性计算处理大数据流
【摘要】 一、为什么需要惰性计算处理日志文件、数据库游标或网络数据流时,一次性把全部数据读进列表往往既占内存又慢。生成器(generator)只在需要时才产生下一个值,用多少算多少,是 Python 里最实用的惰性计算工具。二、迭代器协议:iter 与 next迭代器是实现了 __iter__ 和 __next__ 的对象。iter() 返回迭代器自身,next() 取出下一个值,取完后抛出 Stop...
一、为什么需要惰性计算
处理日志文件、数据库游标或网络数据流时,一次性把全部数据读进列表往往既占内存又慢。生成器(generator)只在需要时才产生下一个值,用多少算多少,是 Python 里最实用的惰性计算工具。
二、迭代器协议:iter 与 next
迭代器是实现了 __iter__ 和 __next__ 的对象。iter() 返回迭代器自身,next() 取出下一个值,取完后抛出 StopIteration。for 循环正是不断调用 next() 直到异常为止的语法糖。
class Countdown:
def __init__(self, n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n <= 0:
raise StopIteration
self.n -= 1
return self.n + 1
for x in Countdown(3):
print(x) # 3 2 1
三、生成器函数:yield 让函数“暂停”
含有 yield 的函数调用后并不执行,而是返回一个生成器;每次 next() 执行到 yield 就暂停并返回值,下次从断点继续,局部状态全部保留。
def read_lines(path):
with open(path, encoding='utf-8') as f:
for line in f:
if line.strip():
yield line.strip()
# 逐行处理 10GB 日志,内存占用始终只有一行
for line in read_lines('app.log'):
if 'ERROR' in line:
print(line)
四、生成器表达式:列表推导的惰性版
把列表推导的方括号换成圆括号,就得到生成器表达式,适合做流水线式处理:
total = sum(int(x) for x in open('nums.txt') if x.strip())
squares = (x * x for x in range(10 ** 8)) # 不占内存,随用随算
五、yield from:委托给子生成器
yield from 把子生成器的产出直接转发出去,还能透传 send() 的值与返回值,写递归遍历、树形展开时特别干净:
def walk(tree):
yield tree['name']
for child in tree.get('children', []):
yield from walk(child)
六、常见坑
第一,生成器只能消费一次,第二次遍历会直接为空,需要重建或用 itertools.tee;第二,不要在生成器里过早关闭资源,with 语句要包在生成器函数内部,调用方只管迭代;第三,send()、throw()、close() 会改变生成器状态,调试时先确认执行到了哪个 yield。
一句话总结:列表是“先算好再给你”,生成器是“用到哪算到哪”。数据量越大、流水线越长,生成器的收益越明显。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)