用Python写迭代器(Iterator)的时候,可能会有人向你推荐生成器(Generator),并列举一堆生成器的好处。不过,今天要来分享一个生成器的坑……
很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!??¤
QQ群:1057034340
假如我们要来写一个归一化函数,简单看个例子:
def normalize(numbers): total = sum(numbers) result = [] for v in numbers: p = 100.0 * v / total result.append(p) return result def main(): data = [12, 20, 35, 9] print normalize(data)
正常情况下输出结果非常符合预期:
[15.789473684210526, 26.31578947368421, 46.05263157894737, 11.842105263157896]
但……
如果 numbers 传入了一个generator,那结果会变成什么样子呢?我们来试一下:
def gen_data(data): for v in data: yield v def main(): data = [12, 20, 35, 9] gen = gen_data(data) print normalize(gen)
猜猜结果输出什么:
[ ] |
没错,是空列表!怎么回事呢?是for循环里的生成器坏了,试试直接输出:
def main(): data = [12, 20, 35, 9] gen = gen_data(data) for v in gen: print v
输出结果:
没问题呀!
那……
排除生成器本身的问题,那肯定是normalize函数错了。断点看一下:
def normalize(numbers): total = sum(numbers) -------> 这里没错,total=76 result = [] for v in numbers: -------> for循环竟然直接跳过了 p = 100.0 * v / total result.append(p) return result
仔细想想,生成器的原理是什么?调用next() 获取下一个元素的值,直到返回 StopIteration。
这样子就说得通了,sum() 把生成器遍历了一遍,等到for循环的时候,已经没有内容需要遍历了。
def main(): data = [12, 20, 35, 9] gen = gen_data(data) t1 = sum(gen) t2 = sum(gen) print t1, t2
结果输出:
改……
这里提供一个简单的解决方法:
class GenData(object): def __init__(self, data): self.data = data def __iter__(self): for v in self.data: yield v
至于为啥,留给你思考一下。