python之路——生成器

来源:互联网 发布:apk签名软件 编辑:程序博客网 时间:2024/06/05 00:35

生成器

列表生成式

顾名思义,列表生成式就是通过某种方式来生成一个列表,那么 OK,我们可以通过哪几种方式来生成呢?

  • map函数的方式
from collections import Iterablelist_1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]def add(x):    return x + 2result = map(add, list_1)print(type(result))print(isinstance(result, Iterable))

print—>:
class ‘map’
True

可以看到返回的结果是一个map类型的可迭代对象,map函数的作用就是将列表中的每一个元素传递到函数中,并将函数返回的结果包装成一个可迭代的对象。

看完这种形式,我们再来看看另外一种形式的高逼格map函数:

from collections import Iterablelist_1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]list_2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]list_3 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]def add(x, y, z):    return x + y + zresult = map(add, list_1, list_2, list_3)for x in result:    print(x)print(type(result))print(isinstance(result, Iterable))

print—>:
0
3
6
9
12
15
18
21
24
27
class ‘map’
True

  • 列表生成式的形式

通过列表生成式的形式,我们可以直接创建一个列表。

list_4 = [x for x in range(0, 10)]print(list_4)

print—>:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

通过这种方式我们创建了一个包含10个元素的列表,如果我们需要创建一个包含100万个元素的列表呢?一般来说计算机的内存都是有限制的,而且创建一个100万个元素的列表,如果我们只用到前面几个,那么显然是很浪费内存空间的,程序的执行效率也会很低的。
so,聪明的人类就会想到,如果不把列表一次性创建出来,而是元素可以通过某种算法推算出来,那岂不是不用占用这么大的内存了。这就是生成器的作用。

  • generator 生成器
    创建一个生成器的方式有很多种,一种简单的方式就是将列表生成式的[]改成(),代码如下:
from collections import Iterableg = (x for x in range(10))g = (x for x in range(10))print(g)print(type(g))print(isinstance(g, Iterable))

print—>:
generator object genexpr at 0x0000022EF0C64E60
class ‘generator’
True

那么我们如何来遍历生成器呢?刚才我们说过生成器并不是一个集合形式的数据类型,我们可以将其理解成可进行推算的一种算法。在python中我们可以通过调用next函数来进行这种推算。
next函数顾名思义就是获取下一个值的意思:

>>> next(g)0>>> next(g)1>>> next(g)4>>> next(g)9>>> next(g)16>>> next(g)25>>> next(g)36>>> next(g)49>>> next(g)64>>> next(g)81>>> next(g)Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration

我们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

for i in g:    print(i)

print—>:
0
1
2
3
4
5
6
7
8
9

所以我们创建好一个generator后,基本都是用for循环来遍历,毕竟next这种方式实在是太麻烦了,所以我们基本不用关心StopIteration异常。

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

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

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

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

def fib(num):    n, a, b = 0, 1, 1    while n < num:        print(a)        a, b = b, a + b        n = n + 1fib(10)

print—>:
1
1
2
3
5
8
13
21
34
55

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

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

def fib(num):    n, a, b = 0, 1, 1    while n < num:        yield (a)        a, b = b, a + b        n = n + 1fib(10)print(fib(10))for i in g:    print(i)

print—>:

1
1
2
3
5
8
13
21
34
55

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

原创粉丝点击