Effective Python(16): 用生成器来改写直接返
2019-10-24 本文已影响0人
warmsirius
前言
如果函数要产生一系列结果,那么最简单的做法就是把这些结果都放在一份列表里,并将其返回给调用者。
1. 举例
例如,我们要查出字符串中每个词的首字母,在整个字符串里的位置。
下面这段代码,用append
方法将这些词的首字母添加到result
列表中,并在函数结束时将其返回给调用者。
def index_word(text):
result = []
if text:
result.append(0)
for index, letter in enumerate(text):
if letter == " ":
result.append(index + 1)
return result
一、返回列表的缺点及改进
列表的缺点
1. 代码写的比较拥挤
每次找到新的结果,都要调用
append
方法。另外,每次append
都需要index+1
,且函数首尾还各有1行代码用来创建及返回result
列表。
生成器
- 生成器是使用
yield
表达式的函数。调用生成器函数时,它并不会真正的运行,而是会返回迭代器。- 每次在这个迭代器上面调用内置的
next
函数时,迭代器会把生成器推进到下一个yield
表达式那里。- 生成器传给
yield
的每一个值,都会由迭代器返回给调用者。
改进:
def index_words_iter(text):
if text:
yield 0
for index, letter in enumerate(text):
if letter == " ":
yield index + 1
调用该生成器后所返回的迭代器,可以传给内置的list
函数,以将其转化为列表。
result = list(index_words_iter(text))
2. 返回前将所有结果都放在列表里面,如过输入量非常大,那么程序可能崩溃
下面定义的这个城生气,会从文件里面依次读入各行内容,然后逐个处理每行中的单词,并产生相应结果。
该函数执行时所耗的内存,由单行输入值的最大字符数来界定。
def index_file(handle):
offset = 0
for line in handle:
if line:
yield offset
for letter in line:
offset += 1
if letter == ' ':
yield offset
运行这个生成器函数,也能产生和原来相同的效果。
from itertools import islice
with open("/tmp/address.txt", "r") as f:
it = index_file(f)
results = islice(it, 0, 3)
print(list(results))
注意
定义生成器的时候注意:函数返回的迭代器是有状态的,调用者不应该反复使用它。