列表推导 假设现在有一个需求,要求我们把列表 [0,1,2,3,4,5,6,7,8,9] 中的每个值加 1,那么一般会有以下几种实现方式: 方法一(直接使用 for 循环,不推荐): - info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- for i, ele in enumerate(info):
- info[i] += 1
- print(info)
复制代码方法二(使用内置的 map 函数): - info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- a = map(lambda x:x+1, info)
- print(a)
复制代码方法三(列表推导): - info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- a = [i+1 for i in range(10)]
- print(a)
复制代码其中,更推荐使用方法三,写法更加的 Pythonic,而且当需要条件过滤时,map 需要借助 filter 函数,使用列表推导的方式更简洁。
生成器 通过上述的列表推导可以方便地创建一个列表,但是需要创建的列表很大时,就会占据很大的内存,极端情况下内存甚至无法容纳,而且如果我们仅仅只是需要访问当中某些元素,更是一个极大的浪费,此时,我们就需要考虑用生成器,而不是直接创建并返回列表。
生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器。
要创建一个生成器,有很多种方法,第一种方法就是把列表推导的 [] 换成 (),就创建了一个生成器: - >>> L = [x * x for x in range(10)]
- >>> L
- [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- >>> g = (x * x for x in range(10))
- >>> g
- <generator object <genexpr> at 0x1022ef630>
复制代码生成器中值无法像列表一样直接打印,需要通过 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
复制代码可以看到,生成器保存的其实是产生元素的算法,每次调用 next(g),就计算出 g 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的错误。因为生成器本身也是可迭代对象,所以一般使用 for 循环: - >>> g = (x * x for x in range(10))
- >>> for n in g:
- ... print(n)
- ...
- 0
- 1
- 4
- 9
- 16
- 25
- 36
- 49
- 64
- 81
复制代码当需要实现的算法比较复杂,列表推导无法很好表达的情况下,则需要通过生成器函数来定义,例如著名的斐波那契数列: - def fib(max):
- n, a, b = 0, 0, 1
- while n < max:
- yield b
- a, b = b, a + b
- n = n + 1
复制代码这就是实现生成器的第二种方式,如果一个函数定义中包含 yield 关键字,那么这个函数就不再是一个普通函数,而是一个生成器。
这里说一下生成器和函数的执行流程,函数是顺序执行的,遇到 return 语句或者最后一行函数语句就返回。而变成生成器的函数,在每次调用 next() 的时候执行,遇到 yield 语句返回,再次被 next() 调用时候从上次的返回 yield 语句处急需执行,也就是用多少,取多少,不占内存。
同样的,既然是生成器,就可以用 for 循环来迭代: - >>> for n in fib(6):
- ... print(n)
- ...
- 1
- 1
- 2
- 3
- 5
- 8
复制代码单总结,Python 提供了两种方法来实现生成器 - 生成器函数:也是用 def 定义的,利用关键字 yield 一次性返回一个结果,阻塞,重新开始
- 生成器表达式:返回一个生成器对象,这个对象只有在需要的时候才产生结果
|