参考:

本文内容是:

先讲最简单的generator创建方式,

然后讲2种generator获取元素方式,

最后讲第二种generator创建方式。


在深度学习中训练模型的过程中读取图片数据,如果将图片数据全部读入内存是不现实的,所以有必要使用生成器来读取数据。

举例,创建一个包含100万个元素的列表,会占用很大的存储空间,而我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。


创建generator有多种方法,第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:


1. >>> L = [x * x for x in range(10)]
2. >>> L
3. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 
1. >>> g = (x * x for x in range(10))

上面简单写了第一种生成generator的方法,那么怎么访问generator里面的元素?也有2种方法,我们在下面一起讲完2种。

 

list中的元素可以直接打印出来 ,generator要一个一个打印出来,可以通过next()函数获得generator的下一个返回值: 


1. >>> next(g)
2. 0
3. >>> next(g)
4. 1
5. >>> next(g)
6. 4
7. >>> next(g)
8. 9
9. >>> next(g)
10. 16

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。上面这种不断调用next(g)实在是太变态了,

正确的方法是使用for循环,因为generator也是可迭代对象:我们常用这种形式,获得generator的元素

>>> g = (x * x for x in range(10))
 >>> for n in g:
 ...     print(n)


---------------------举例子----------------------------------------


著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ... 

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易: 

def fib(max):
     n, a, b = 0, 0, 1
     while n < max:
         print(b)
         a, b = b, a + b
         n = n + 1
     return 'done'

这里开始讲generator的第2种创建方式,至于generator的元素遍历方法上面已经一次都讲完了2种方法:next(generator)或者‘for 遍历 generator对象’

上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
     n, a, b = 0, 0, 1
     while n < max:
         yield b
         a, b = b, a + b
         n = n + 1
     return 'done'


这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

>>> f = fib(6)
 >>> f
 <generator object fib at 0x104feaaa0>

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句处返回,再次执行时从上次返回的yield语句处继续执行。

在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代

>>> for n in fib(6):
 ...     print(n)
 ... 

理解这2个例子,基本就没问题了。

next():读取生成器内容

def bylineread(fimename):
     with open(fimename) as f:
         line = f.readline()
         while line:
             yield line
             line = f.readline()
 #read是一个生成器对象
 read = bylineread('data.txt')
 print(read)
 #1.next读取生成器内容
 print(next(read))
 print(next(read))
 ---------------------

for遍历循环读取

def bylineread(fimename):
     with open(fimename) as f:
         line = f.readline()
         while line:
             yield line
             line = f.readline()
 #read是一个生成器对象
 read = bylineread('data.txt')
 print(read)
 #for
 for item in read:
     print(item)
 ---------------------