Python 迭代器、生成器与yield

2018-10-26  本文已影响24人  filterc

一张图概览:

relations

container:容器

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。

比如常用的list、set、dict等。

iterable:可迭代对象

很多容器都是可迭代对象,此外还有,比如处于打开状态的files,sockets等等,也是可迭代的对象。

可迭代对象是一个通用的说法,但凡是可以返回一个迭代器的对象都可称之为可迭代对象,也就是调用iter()可以返回一个iterator。

iterator:迭代器

迭代器是一个带状态的对象,任何实现了iternext()(python2中实现next())方法的对象都是迭代器。
iter返回迭代器自身,next返回容器中的下一个值。
如果容器中没有更多元素了,则抛出StopIteration异常。

iterable-vs-iterator.png
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()方法的时候做两件事:

迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

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的函数不仅仅只用于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'

参考:

上一篇下一篇

猜你喜欢

热点阅读