【微语】不要争论所谓的小事,即使赢了也得不到好处。


生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器。

既然生成器这么NB,那我们先说说列表生成式。

列表生成式

列表的生成方式有下面三种: (1)、用[]定义,比如a = [1,2,3,4]; (2)、用list()方法生成,比如a = list((1,2,3,4)); (3)、用列表生成式 我们来看看列表生成式的基本语法:

[exp for iter_var in iterable]

其工作过程如下: (1)、迭代iterable中的每个元素; (2)、每次迭代都先把结果赋值给iter_var,然后通过exp得到一个新的计算值; (3)、最后把所有通过exp得到的计算值以一个新列表的形式返回。 其过程相当于下面这种语法:

L = []

for iter_var in iterable:

    L.append(exp)

带过滤功能的列表生成式的基本语法:

[exp for iter_var in iterable if_exp]

其工作过程如下: (1)、迭代iterable中的每个元素,每次迭代都先判断ifexp表达式结果为真,如果为真则进行下一步,如果为假则进行下一次迭代; (2)、把迭代结果赋值给itervar,然后通过exp得到一个新的计算值; (3)、最后把所有通过exp得到的计算值以一个新列表的形式返回; 其过程相当于下面这个例子:

L = []

for iter_var in iterable:

    if_exp:

        L.append(exp)

循环嵌套的列表生成式的格式:

[exp for iter_var_A in iterable_A for iter_var_B in iterable_B]

其工作过程是每迭代iterableA中的一个元素,就把ierableB中的所有元素都迭代一遍。 相当于下面这种写法:

L = []

for iter_var_A in iterable_A:

    for iter_var_B in iterable_B:

        L.append(exp)

通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。这时候生成器在这里就发挥作用了。

生成器

在python中使用yield的函数称为生成器。如下:

def count(n):

    while n > 0:

        yield n

        n -= 1

跟普通函数不同的是生成器返回的是一个迭代器函数,只能用于迭代操作,可以说生成器就是一个迭代器。在调用生成器运行的过程中,每次运行到yield会暂停并保存所有的运行信息并返回yield的值,在下次调用next()方法的时候当前的位置继续运行。 例如:

# 用普通函数创建斐波拉契函数

>>> def fib1(n):

...     a, b, count = 0, 1, 0

...     while count < n:

...             print(a)

...             a, b = b, a+b

...             count += 1

...

>>> fib1(10)

0

1

1

2

3

5

8

13

21

34

>>>


# 用生成器创建

>>> def fib2(n):

...     a,b,count = 0,1,0

...     while count<n:

...             yield a

...             a,b=b,a+b

...             count += 1

...

>>> for i in fib2(10):

...     print(i)

...

0

1

1

2

3

5

8

13

21

34

作为生成器,因为每次迭代就会返回一个值,所以不能显示的在生成器函数中return 某个值,包括None值也不行,否则会抛出“SyntaxError”的异常,但是在函数中可以出现单独的return,表示结束该语句。 通过固定长度的缓冲区不断读文件,防止一次性读取出现内存溢出的例子:

def read_file(path):  

    size = 1024  

    with open(path,'r') as f:  

        while True:  

            block = f.read(SIZE)  

            if block:  

                yield block

            else:  

                return  

从上可以看出生成器的优点了吗?