python3 生成器

generator

如果一个函数至少包含一个yield声明(当然它也可以包含其他yield或return),那么它就是一个generator。

yield和return都会让函数返回一些东西,区别在于,return声明彻底结束一个函数,而yield声明是暂停函数,保存它的所有状态,并且后续被调用后会继续执行。

generator函数和普通函数的区别

  • generator函数包含一个以上的yield声明
  • generator函数被调用的时候,会返回一个iterator对象,但是函数并不会立即开始执行
  • __iter__()和__next__()方法被自动实现,所以可以使用next()函数对返回的此iterator对象进行迭代
  • 一旦一个generator 执行到yield语句,generator函数暂停,程序控制流被转移到调用方
  • 在对generator的连续调用之间,generator的本地变量和状态会被保存
  • 最终,generator函数终止,再调用generator会引发StopIteration异常

一个generator示例

def my_gen():  
    n = 1  
    print('This is printed first')  
    # Generator function contains yield statements  
    yield n  
  
    n += 1  
    print('This is printed second')  
    yield n  
  
    n += 1  
    print('This is printed at last')  
    yield n  

迭代:

a = my_gen()
print(next(a))
print(next(a))
print(next(a))

输出:

This is printed first
1
This is printed second
2
This is printed at last
3

在这个例子里变量n在每次调用之间都被记住了。和一般函数不同的是,在函数yield之后本地变量没有被销毁,而且,generator对象只能被这样迭代一次。

for循环迭代:

for item in my_gen():
    print(item)

输出:

This is printed first
1
This is printed second
2
This is printed at last
3

有循环的generator

通常来说,generator都是和循环结合实现的,且这个循环带有一个终止条件。

def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1, -1, -1):
        yield my_str[i]

for char in rev_str("hello"):
     print(char)  

输出:

o
l
l
e
h

generator 表达式

使用generator表达式可以很容易地创建简单的generator。

就像lambda函数可以创建匿名函数一样,generator函数创建一个匿名generator函数。

generator表达式的语法类似于python的list comprehension,只是方括号被替换为了圆括号而已。

list comprehension和generator表达式的主要区别在于,前者产生全部的list,后者每次仅产生一项。

generator有些懒惰,仅在接到请求的时候才会产生输出。因此,generator表达式比list comprehension更加节省内存。

my_list = [1, 3, 6, 10]
#列表生成式
L = [x**2 for x in my_list]
print(L) #[1, 9, 36, 100]
#生成器
a = (x**2 for x in my_list)
print(next(a)) #1
print(next(a)) #9
print(next(a)) #36
print(next(a)) #100

使用generator的优势

容易实现

相对于iterator类来说,generator的实现清晰、简洁。下面是用iterator实现一个2的指数函数:

class PowTwo:
    def __init__(self, max=0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n > self.max:
            raise StopIteration

        result = 2 ** self.n
        self.n += 1
        return result  

generator这样实现:

def PowTwoGen(max = 0):  
    n = 0  
    while n < max:  
        yield 2 ** n  
        n += 1  

generator自动跟踪实现细节,因此更加清晰、简洁。

节省内存

一个函数返回一个序列(sequence)的时候,会在内存里面把这个序列构建好再返回。如果这个序列包含很多数据的话,就过犹不及了。

而如果序列是以generator方式实现的,就是内存友好的,因为他每次只产生一个item。

代表无限的stream

generator是一个很棒的表示无限数据流的工具。无限数据流不能被保存在内存里面,并且因为generator每次产生一个item,它就可以表示无限数据流。

下面的代码可以产生所有的奇数:

def all_even():  
    n = 0  
    while True:  
        yield n  
        n += 2  

 

posted @ 2018-11-26 11:11  刘-皇叔  阅读(585)  评论(0编辑  收藏  举报