9

Python生成器详解

 3 years ago
source link: http://c.biancheng.net/view/2393.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Python生成器详解

前面章节中,已经详细介绍了什么是迭代器。生成器本质上也是迭代器,不过它比较特殊。

以 list 容器为例,在使用该容器迭代一组数据时,必须事先将所有数据存储到容器中,才能开始迭代;而生成器却不同,它可以实现在迭代的同时生成元素。

也就是说,对于可以用某种算法推算得到的多个数据,生成器并不会一次性生成它们,而是什么时候需要,才什么时候生成。

不仅如此,生成器的创建方式也比迭代器简单很多,大体分为以下 2 步:
  1. 定义一个以 yield 关键字标识返回值的函数;
  2. 调用刚刚创建的函数,即可创建一个生成器。
举个例子:
  1. def intNum():
  2. print("开始执行")
  3. for i in range(5):
  4. yield i
  5. print("继续执行")
  6. num = intNum()
def intNum():
    print("开始执行")
    for i in range(5):
        yield i
        print("继续执行")
num = intNum()
由此,我们就成功创建了一个 num 生成器对象。显然,和普通函数不同,intNum() 函数的返回值用的是 yield 关键字,而不是 return 关键字,此类函数又成为生成器函数。

和 return 相比,yield 除了可以返回相应的值,还有一个更重要的功能,即每当程序执行完该语句时,程序就会暂停执行。不仅如此,即便调用生成器函数,Python 解释器也不会执行函数中的代码,它只会返回一个生成器(对象)。

要想使生成器函数得以执行,或者想使执行完 yield 语句立即暂停的程序得以继续执行,有以下 2 种方式:
  1. 通过生成器(上面程序中的 num)调用 next() 内置函数或者 __next__() 方法;
  2. 通过 for 循环遍历生成器。
例如,在上面程序的基础上,添加如下语句:
  1. #调用 next() 内置函数
  2. print(next(num))
  3. #调用 __next__() 方法
  4. print(num.__next__())
  5. #通过for循环遍历生成器
  6. for i in num:
  7. print(i)
#调用 next() 内置函数
print(next(num))
#调用 __next__() 方法
print(num.__next__())
#通过for循环遍历生成器
for i in num:
    print(i)
程序执行结果为:

开始执行
0
继续执行
1
继续执行
2
继续执行
3
继续执行
4
继续执行

这里有必要给读者分析一个程序的执行流程:
1) 首先,在创建有 num 生成器的前提下,通过其调用 next() 内置函数,会使 Python 解释器开始执行 intNum() 生成器函数中的代码,因此会输出“开始执行”,程序会一直执行到yield i,而此时的 i==0,因此 Python 解释器输出“0”。由于受到 yield 的影响,程序会在此处暂停。

2) 然后,我们使用 num 生成器调用 __next__() 方法,该方法的作用和 next() 函数完全相同(事实上,next() 函数的底层执行的也是 __next__() 方法),它会是程序继续执行,即输出“继续执行”,程序又会执行到yield i,此时 i==1,因此输出“1”,然后程序暂停。

3) 最后,我们使用 for 循环遍历 num 生成器,之所以能这么做,是因为 for 循环底层会不断地调用 next() 函数,使暂停的程序继续执行,因此会输出后续的结果。

注意,在 Python 2.x 版本中不能使用 __next__() 方法,可以使用 next() 内置函数,另外生成器还有 next() 方法(即以 num.next() 的方式调用)。

除此之外,还可以使用 list() 函数和 tuple() 函数,直接将生成器能生成的所有值存储成列表或者元组的形式。例如:
  1. num = intNum()
  2. print(list(num))
  3. num = intNum()
  4. print(tuple(num))
num = intNum()
print(list(num))

num = intNum()
print(tuple(num))
程序执行结果为:

开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
[0, 1, 2, 3, 4]
开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
(0, 1, 2, 3, 4)

通过输出结果可以判断出,list() 和 tuple() 底层实现和 for 循环的遍历过程是类似的。

相比迭代器,生成器最明显的优势就是节省内存空间,即它不会一次性生成所有的数据,而是什么时候需要,什么时候生成。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK