生成器可以认为是一个特殊的迭代器,用途也是对特定数据序列进行逐个遍历,只不过生成器是 Python 在平台层面实现了通用的迭代器逻辑,在编码时开发者只需要通过 yield 关键字即可快速构建迭代器,符合 Python 简洁的风格。
Python高级-迭代器 一文中通过迭代器实现了逐个遍历一个英文句子中的单词的例子
如下是通过生成器实现的简洁版本:
import re
import reprlib
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for word in self.words:
yield word
s = Sentence('"The time has come," the Walrus said')
for word in s:
print(word)
生成器的核心原理如下:
- 在函数中存在 yield 关键字,则该函数会被底层认为是一个生成器函数,调用该函数会返回一个生成器对象
- 生成器与迭代器在使用上没有差别,都可以通过 for 循环或者 next() 函数进行迭代
- 程序在执行到 yield 关键字后会暂停函数(保留上下文),并返回 yield 后的变量值,在下一次迭代动作触发后继续执行,直到函数所有代码执行完毕后会自动抛出 StopIteration 异常作为迭代结束的信号
同时生成器还具备惰性加载的特性,在处理海量数据时,对于节省内存非常有效。
惰性加载允许程序运行过程中按需,分批地加载数据资源,在之前实现的代码中并没有利用这个特性,在 Sentence 对象初始化阶段就完成了全量单词列表的构建(想象一下,如果这个单词列表的数据巨大很有可能就把内存撑爆了)。
如下是一个惰性生成器实现版本:按需去逐个匹配句子中的单词,这也依赖于 RE_WORD.finditer(self.text) 支持返回一个迭代器
# 惰性生成器
import re
import reprlib
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return f'Sentence({reprlib.repr(self.text)})'
def __iter__(self):
for match in RE_WORD.finditer(self.text):
yield match.group()
s = Sentence('"The time has come," the Walrus said')
for word in s:
print(word)