摄影:产品经理比起法餐,我还是更喜欢烧烤和火锅粉
我们知道,生成器函数初始化的时候,它返回的是一个生成器。里面的代码一开始是没有运行的。只有当这个生成器被迭代的时候,才会运行:
正是由于这个原理,如果没有理解就贸然使用,可能会产生你难以理解的 bug。我们来看一个例子:
def do_filter(datas, to_filter): for data in datas: if to_filter in data: continue yield data def trash_filter(): datas = ['有效信息', '重要信息', '隐私信息', '脏数据', '保密信息', '无效数据', '大数据'] result = do_filter(datas, '无效') result = do_filter(result, '脏') return result for data in trash_filter(): print(data)
在这段代码里面,我们有两个生成器,它们分别过滤无效
和脏
。
我们再来看第二段代码:
def do_filter(datas, to_filter): for data in datas: if to_filter in data: continue yield data def trash_filter(): datas = ['有效信息', '重要信息', '隐私信息', '脏数据', '保密信息', '无效数据', '大数据'] for word in ['无效', '脏']: result = do_filter(datas, word) return result for data in trash_filter(): print(data)
运行效果如下图所示:
可以看到,在这段代码只是把原来的分两次调用改成了使用 for 循环,但是却发现过滤出问题了,无效数据
没有被正确过滤。
要解释这个问题的原因,我们还要从生成器的延迟执行说起。回到最开始的代码,我们修改一下,增加一个外部变量:
outer = 123 def i_am_generator(): print('生成器函数运行了。外部变量的值为:', outer) yield generator = i_am_generator()print(f'现在返回的是一个生成器对象:{generator}') outer = 456for _ in generator: ...
运行效果如下图所示:
一开始外部变量outer
的值为123,然后我执行生成器函数,创建一个生成器。然后修改外部变量的值为456.最后再对生成器进行迭代。可以看到,迭代生成器的时候,生成器内部的代码才会真正去读这个外部变量。而它读的时候,外部变量已经被改成了456。
所以,对于代码:
for word in ['无效', '脏']: result = do_filter(datas, word)
第一次循环 word
的值确实取到了无效
,但是result = do_filter(datas, word)
只是返回了一个生成器,这个生成器没有执行,所以它没有读取 word
变量里面的值。第二次循环,word
变量的值变成了脏
,此时第二次生成了新的生成器。
最后返回的是第二个生成器。当我们对第二个生成器进行迭代的时候,它才真正读取了脏
这个值。
所以,上面这段代码与下面这段代码是等效的:
result = do_filter(datas, '脏')result = do_filter(datas, '脏')
所以只会过滤包含脏
字的内容,却不会过滤包含无效
的内容。