迭代器,生成器,yield,yield from理解

迭代器

说到迭代器就得想说可迭代对象Iterable,实现了__iter__()方法的对象都是可迭代对象,例如很多容器,list ,set, tuples。使用iter方法可以把一个可迭代对象变成迭代器

迭代器是实现了__next__()方法的可迭代对象,也就是说迭代器必须实现__iter__()和__next__()方法,迭代器可以调用next()放不断的迭代,在给定的范围中返回数据,超出范围抛出异常。

几个有趣的例子:

from itertools import count
counter = count(start=13)
next(counter)
13
next(counter)
14   无限序列只要你愿意可以一直迭代下去,然而你无法创建一个这么大的list是不是可以说的迭代器的优点呢
from itertools import cycle
colors = cycle(['red', 'white', 'blue'])
print next(colors)
# 'red'
print next(colors)
# 'white'
print next(colors)
# 'blue'
print next(colors)
# 'red'   无限循环序列
from itertools import islice,cycle
colors = cycle(['red', 'white', 'blue'])
limited = islice(colors, 0, 6)            
for x in limited:
    print(x)
#有限序列
from itertools import islice
class Fib:
    def __init__(self):
        self.prev = 0
        self.curr = 1
    def __iter__(self):
        return self

    def __next__(self): #注意在python2中需要写成next() python3中写成__next__()
        value = self.curr
        self.curr += self.prev
        self.prev = value
        return value
f = Fib()
print(list(islice(f, 0, 10)))

生成器

简单的生成器(x for x in range(5)) 和列表生成式很像,如果生成的逻辑比较复杂,用类似列表生成式的方式难以表达,我们可以用函数加关键字yield的方式。

在我的理解中,生成器generator是一种特殊的迭代器,他用yield来自动实现了__iter__()和__next__()方法

那我们用yield来实现一下斐波拉契:

def fib():
    prev = 0
    curr = 1
    while True:
        value = curr
        yield value
        curr += prev
        prev = value
a = fib()
for x in range(10):
    print(next(a))

yield 

这个yield这么神奇吗?到底该怎么理解它呢?首先你可以把它当成一个return,之后就不再继续往下执行了,调用next()方法后继续执行,举个例子。

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))

starting...
4    # 走到yield 4的时候马上return 4(马上返回并没有赋值操作),
********************
res: None  #接着 yield 继续往下走,进行赋值操作,4已经被return 出去了,所以res=None
4  # 进入下一个循环,又走到yield 4

我们稍微修改一下函数

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))

starting...   
4
********************
res: 7   #挺神奇的怎么变成7了,注意我们此时用的是send()方法,它是传递了一个值给yield表达式,相当于res=7
4

yield from 

yield 是从生成器里返回一个元素,那么yield from 从名字上理解就应该是返回一个生成器咯

def foo():
    yield from [1,2,3]

a = foo()  
print(next(a))
print(next(a))
print(next(a))
结果:

1
2
3

是不是就可以理解为yield from 返回的是 iter([1,2,3])呢

下面看两个例子

def foo():
    for i in range(5):
        if i == 2:
            return 'gg'
        else:
            yield i
a = foo()
for i in a:
    print(i)

0
1

def foo():
    for i in range(5):
        if i == 2:
            return 'gg'
        else:
            yield i
a = foo()
for i in range(5):
    print(next(a))
0
1
StopIteration: gg

这是yield的特性,在使用for循环取遍历生成器的时候是不会触发StopIteration异常,使用next()可以触发,如果我们又想用for循环遍历又想触发异常怎么弄,再封装一下

def foo():
    for i in range(5):
        if i == 2:
            return 'gg'
        else:
            yield i

def fun(generator):
    res = yield from generator
    print(res)

a = foo()
b = fun(a)
for x in b:
    print(x)

0
1
gg   # 捕捉到了异常并且把return 的值给了yield from 表达式

 

posted @ 2019-08-08 15:19  君子不徒语  阅读(233)  评论(0)    收藏  举报