Python生成器

生成器详解

1.什么是迭代器

只要定义了__iter__的类那么他就是一个可迭代对象,如果定义了__next__方法那么他就是一个迭代器.

2.迭代器与生成器的区别

生成器是一种迭代器,但是迭代器不一定是生成器

3.什么是生成器

简单理解为每次生出(yield)一个值的东西

例子:

def generator():
    for i in range(3):
        yield i

这个函数就是一个生成器,与普通函数不同的是,当函数执行到yield i语句时,函数会被挂起并且把yield后面的i给返回,当你再次执行该函数时会从yield语句后面接着执行

不同于return语句的是,return语句不会挂起

4.生成器的优点

由于生成器每调用一次就只执行一次,所以生成器占用得到内存就会非常少,看个循环100000000次的例子

In [185]: def func(): 
     ...:     for i in range(100000000): 
     ...:         pass 
     ...:                                                                                         

In [186]: def gene(): 
     ...:     for i in range(100000000): 
     ...:         yield 
     ...:                                                                                         

In [187]: def getTime(f): 
     ...:     start = time.time() 
     ...:     f() 
     ...:     end = time.time() 
     ...:     print(end-start) 
     ...:                                                                                         

In [188]: getTime(func)                                                                           
2.0609002113342285

In [189]: getTime(gene)                                                                           
2.6226043701171875e-06

5.生成器的使用

可以使用next()函数或者__next__()方法来取值,当到最后一个值时会产生异常.

也可使用for语句取值,并且for语句会自动处理这个异常.

next()例子:

In [146]: def generator(): 
     ...:     for i in range(3): 
     ...:         yield i 
     ...:                                                                                         

In [147]: g = generator()                                                                         

In [148]: next(g)                                                                                 
Out[148]: 0

In [149]: next(g)                                                                                 
Out[149]: 1

In [150]: next(g)                                                                                 
Out[150]: 2

In [151]: next(g)                                                                                 
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-151-e734f8aca5ac> in <module>
----> 1 next(g)

StopIteration: 

__next__()例子:

In [146]: def generator(): 
     ...:     for i in range(3): 
     ...:         yield i 
     ...:                                                                                         

In [147]: g = generator()                                                                         

In [148]: next(g)                                                                                 
Out[148]: 0

In [149]: next(g)                                                                                 
Out[149]: 1

In [150]: next(g)                                                                                 
Out[150]: 2

In [151]: next(g)                                                                                 
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-151-e734f8aca5ac> in <module>
----> 1 next(g)

StopIteration: 

for例子:

In [146]: def generator(): 
     ...:     for i in range(3): 
     ...:         yield i 
     ...:                                                                                         

In [147]: g = generator()                                                                         

In [148]: next(g)                                                                                 
Out[148]: 0

In [149]: next(g)                                                                                 
Out[149]: 1

In [150]: next(g)                                                                                 
Out[150]: 2

In [151]: next(g)                                                                                 
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-151-e734f8aca5ac> in <module>
----> 1 next(g)

StopIteration: 

注意:不要这样写

In [162]: def generator(): 
     ...:     for i in range(3): 
     ...:         yield i 
     ...:                                                                                         

In [163]: next(generator())                                                                       
Out[163]: 0

In [164]: next(generator())                                                                       
Out[164]: 0

In [165]: next(generator())                                                                       
Out[165]: 0

直接调用生成器,每次会从头开始,这样就永远只能取到第一个值.(但是for循环可以使用)

In [162]: def generator(): 
     ...:     for i in range(3): 
     ...:         yield i 
     ...:                                                                                         

In [166]: for i in generator(): 
     ...:     print(i) 
     ...:                                                                                         
0
1
2

6.可迭代对象与迭代器

可迭代对象不一定是迭代器

str是可迭代对象,但是他不是迭代器:

	
In [191]: string = "abcdefg"                                                                      

In [192]: for i in string: 
     ...:     print(i) 
     ...:                                                                                         
a
b
c
d
e
f
g

In [193]: next(string)                                                                            
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-193-50005732e4cf> in <module>
----> 1 next(string)

TypeError: 'str' object is not an iterator

将可迭代对象转换成迭代器:

In [194]: string = iter(string)                                                                   

In [195]: next(string)                                                                            
Out[195]: 'a'

In [196]: next(string)                                                                            
Out[196]: 'b'

In [197]: next(string)                                                                            
Out[197]: 'c'

7.send函数

使用send函数可以向生成器发送数据,yield左边的变量会接收:

In [205]: def generator(): 
     ...:     for i in range(10): 
     ...:         recv = yield i 
     ...:         print(recv) 
     ...:                                                                                         

In [206]: g = generator()                                                                         

In [207]: next(g)                                                                                 
Out[207]: 0

In [208]: next(g)                                                                                 
None
Out[208]: 1

In [209]: g.send("hahaha")                                                                        
hahaha
Out[209]: 2

In [210]: g.__next__()                                                                            
None
Out[210]: 3

In [211]: g.__next__()                                                                            
None
Out[211]: 4

对输出结果的解释:

  1. 为什么第一次调用next没有打印东西?

    因为代码在yield语句处挂起,没有执行到print语句

  2. 为什么有些地方得到输出是None

    Noneprint语句的输出结果,输出为None是因为recv变量没有接收到数据

注意:

第一次执行就使用send会报错:

In [213]: def generator(): 
     ...:     for i in range(10): 
     ...:         recv = yield i 
     ...:         print(recv) 
     ...:                                                                                         

In [214]: g = generator()                                                                         

In [215]: g.send("hahahaha")                                                                      
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-215-046ffd60433c> in <module>
----> 1 g.send("hahahaha")

TypeError: can't send non-None value to a just-started generator

错误提示为:不能将一个非None的数据传给刚开始的生成器.

那么就可以这么写

In [216]: g.send(None)                                                                            
Out[216]: 0

注意:什么都不写不代表None

8.协程

协程是一种多任务(让人感觉同一时间发生了多种事件)的实现方式,生成器可以实现协程

例:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

def func1():
    while True:
        yield
        print("----func1被调用----")


def func2():
    while True:
        yield
        print("----func2被调用----")


def main():
    f1 = func1()
    f2 = func2()
    while True:
        next(f1)
        next(f2)


if __name__ == '__main__':
    main()

output:

----func1被调用----
----func2被调用----
----func1被调用----
----func2被调用----
----func1被调用----
----func2被调用----
----func1被调用----
----func2被调用----
----func1被调用----
----func2被调用----
----func1被调用----

...

原文地址:https://www.cnblogs.com/kainhuck/p/11113804.html