zoukankan      html  css  js  c++  java
  • Python生成器介绍(原理+实践)

    本篇博文讲解生成器,主要介绍2个方面,其一为原理,其二为完整代码运行及注解说明。

    一.生成器原理

    生成器却不同,它可以实现在迭代的同时生成元素。

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

    不仅如此,生成器的创建方式也比迭代器简单很多,大体分为以下 2 步:

    1. 定义一个以 yield 关键字标识返回值的函数;
    2. 调用刚刚创建的函数,即可创建一个生成器;

    举个例子:

    1. def intNum():
    2. print("开始执行")
    3. for i in range(5):
    4. yield i
    5. print("继续执行")
    6. num = intNum()

    由此,我们就成功创建了一个 num 生成器对象。显然,和普通函数不同,number() 函数的返回值用的是 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)

    程序执行结果为:

    开始执行
    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))

    程序执行结果为:

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

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

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

    二.完整代码说明:

    # 带有参数的生成器,参数为k
    def intNum(k):
    print("开始执行")
    n=0
    while n<4:
    n=n+1
    yield n+5+k
    print("继续执行")
    num = intNum(3)
    # 说明参数k=3保持不变,每次迭代都不会改变,
    # 而n将会在每次迭代中一直+1,当n<10满足条件
    # 就终止迭代。
    for i in num:
    # 这里i是num函数返回的值,即n+5+k
    print(i)
    # 注:第一运行将执行wile上面的代码,随后在yield处停止,
    # 当再次迭代时将执行yield后面的代码(print("继续执行")),
    # 然后在从while处执行直到遇到yield后,又重复上述步骤执行

    运行结果:

    原理主要借鉴(很详细):http://c.biancheng.net/view/2393.html

  • 相关阅读:
    XCOJ1250: 语言战争
    XCOJ 1249: 全自动奖学金计算系统
    杭电 2159 fate(二维背包费用问题)
    杭电 1059 Dividing
    背包问题的模版
    Shell解析器
    Shell简介
    SpringBoot配置文件占位符
    @ImportResource、@Bean
    @ImportResource注解;
  • 原文地址:https://www.cnblogs.com/tangjunjun/p/12305620.html
Copyright © 2011-2022 走看看