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))

注意

定义生成器的时候注意:函数返回的迭代器是有状态的,调用者不应该反复使用它

上一篇下一篇

猜你喜欢

热点阅读