一张图概览:
container:容器
容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in
, not in
关键字判断元素是否包含在容器中。
比如常用的list、set、dict等。
iterable:可迭代对象
很多容器都是可迭代对象,此外还有,比如处于打开状态的files,sockets等等,也是可迭代的对象。
可迭代对象是一个通用的说法,但凡是可以返回一个迭代器的对象都可称之为可迭代对象,也就是调用iter()可以返回一个iterator。
iterator:迭代器
迭代器是一个带状态的对象,任何实现了iter和next()(python2中实现next())方法的对象都是迭代器。
iter返回迭代器自身,next返回容器中的下一个值。
如果容器中没有更多元素了,则抛出StopIteration异常。
x = [1, 2, 3]
for elem in x:
print elem
# x是一个list,容器,同时也是可迭代对象
# 这段代码,在运行时,会调用iter(x)获得一个迭代器
# 然后对迭代器调用next(...)
>>> x = [1, 2, 3]
>>> y = iter(x)
>>> z = iter(x)
>>> next(y)
1
>>> next(y)
2
>>> next(z)
1
>>> type(x)
<class 'list'>
>>> type(y)
<class 'list_iterator'>
y和z是两个独立的迭代器,状态(也就是调用next返回的值)是分开存储的。
from itertools import slice
class Fib:
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
value = self.curr
self.curr += self.prev
self.prev = value
return value
>>> f = Fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Fib是一个迭代器。实例变量prev和curr用户维护迭代器内部的状态。每次调用next()方法的时候做两件事:
- 为下一次调用next()方法修改状态
- 为当前这次调用生成返回结果
迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。
generator:生成器
生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写iter()和next()方法了,只需要一个yiled关键字。
def fib():
prev, curr = 0, 1
while True:
yield cure
prev, curr = curr, curr + prev
>>> f = fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
fib就是一个普通的python函数,特殊的地方在于没有return关键字,函数的返回值是一个生成器对象。
当执行f=fib()返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。
# 可用生成器重构的典型结构
def something():
result = []
for ... in ...:
result.append(x)
return result
# 可替换为
def iter_something():
for ... in ...:
yield x
generator expression 生成器表达式
生成器不仅可以用函数实现,也可以用表达式来实现。
# 通过列表来创建生成器
[i for i in xrange(10)]
# 等同于
# 通过`yield`来创建生成器
def func():
for i in xrange(10);
yield i
yield
带有 yield 的函数不再是一个普通函数,而是一个生成器generator。
yield 是一个类似 return 的关键字,迭代一次遇到yield时就返回yield后面的值,并且记住当前代码的位置,并在下一次迭代时,从yield后面的代码开始执行。
def simpleYield(n):
for i in range(n):
yield call(i)
print "simpleYield: %d" %i
print("end.")
def call(i):
print 'call: %d' %i
return i * 2
def testYield():
for i in simpleYield(3):
print 'testYeild: %d' %i
## 运行结果
call: 0
testYeild: 0
simpleYield: 0
call: 1
testYeild: 2
simpleYield: 1
call: 2
testYeild: 4
simpleYield: 2
end.
可以看到:
- 在第一次运行的时候,遇到yield,调用了call,然后返回了
- 第二次运行的时候,从yield后面的 print "simpleYield: %d" %i 开始运行
另外,带有yield的函数不仅仅只用于for循环中,而且可用于某个函数的参数,只要这个函数的参数允许迭代参数。比如array.extend函数,它的原型是array.extend(iterable)。
常用生成器工具
生成器常用来生成序列,而又不用将所有列表都保存在内存里。
# 生成无限序列:
>>> from itertools import count
>>> counter = count(start=13)
>>> next(counter)
13
>>> next(counter)
14
# 从一个有限序列中生成无限序列:
>>> from itertools import cycle
>>> colors = cycle(['red', 'white', 'blue'])
>>> next(colors)
'red'
>>> next(colors)
'white'
>>> next(colors)
'blue'
>>> next(colors)
'red'
参考: