可迭代对象泛指一类对象,不是指的每一种对象,确切的说满足以下的条件的对象可以成为可迭代对象:
我们比较容易理解的可迭代对象,比如说可以用for语句去遍历,实际for语句的内部实现应该就是首先调用对象的__iter__方法,获取一个迭代器对象,接着不停的调用迭代器对象的__next__方法,循环遍历取值。
迭代器协议包括这些条件:
能够迭代的类型:list tuple string set dict bytes迭代器有两个基本的方法:iter() 和 next()
创建一个返回数字的迭代器,初始值为 1,逐步递增 1:
class MyNumbers: def __iter__(self): self.a = 1 return self def __next__(self): x = self.a self.a += 1 return x myclass = MyNumbers() myiter = iter(myclass) print(next(myiter)) print(next(myiter)) print(next(myiter)) print(next(myiter)) print(next(myiter))
输出:
1 2 3 4 5
可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己的内置的__iter__方法),所以生成器就是可迭代对象
常规函数的定义,但是,使用yield语句而不是return语句返回结果。yield语句语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器最大的特点是:边迭代 边输出
Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生产层器的主要好处。
这里举一个例子来说明生成器的优点
输出斐波那契数列前 N 个数
def fab(max): n, a, b = 0, 0, 1 L = [] while n < max: L.append(b) a, b = b, a + b n = n + 1 return L for n in fab(5): print n
该函数在运行中占用的内存会随着参数 max 的增大而增大,如果要控制内存占用,最好不要用 List来保存中间结果,而是通过 iterable 对象来迭代。
class Fab(object): def __init__(self, max): self.max = max self.n, self.a, self.b = 0, 0, 1 def __iter__(self): return self def next(self): if self.n < self.max: r = self.b self.a, self.b = self.b, self.a + self.b self.n = self.n + 1 return r raise StopIteration() for n in Fab(5): print n
Fab 类通过 next() 不断返回数列的下一个数,内存占用始终为常数。
然而,使用 class 改写的这个版本,代码远远没有第一版的 fab 函数来得简洁。如果我们想要保持第一版 fab 函数的简洁性,同时又要获得 iterable 的效果,yield 就派上用场了:
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b # 使用 yield # print b a, b = b, a + b n = n + 1 for n in fab(5): print n
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(i) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。
这里记录一个引出本篇博客的问题:
Scrapy中yield的理解
https://www.oschina.net/question/2254016_238539