• 浅析Python生成器


    1、什么是生成器

    在介绍生成器之前,我们先来看一个简单的例子:创建一个列表,列表中存放[0, 9]范围内每个整数平方值

    >>> L = [x * x for x in range(10)]
    >>> L
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    显然,通过列表生成式,很容易的创建了一个这样的列表。但是,我们想创建一个更大的列表,受内存的限制,列表的容量肯定是有限的。比如我们想创建一个包含10万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

    有没有这样的一种机制呢?比如,列表中的元素可以按照某种算法推算出来,我们可以根据自己的需求去取,取的时候根据算法推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。我们今天的讲的生成器就实现了这种机制。

    那什么是生成器呢?可以用一句话概括:在Python中,这种一边循环一边计算的机制,就是生成器(generator)。

    2、生成器的创建

    Python中要创建一个生成器,有很多种方法,先来看一下最简单的一种,只要把一个列表生成式中的[ ]换成(),返回的不再是一个列表,而是一个生成器对象。

    >>> G = (x * x for x in range(10))
    >>> G
    <generator object <genexpr> at 0x0000000004CA8948>

    那么如何根据生成器对象,获取其中的元素呢?

    如果一个个的获取,可以通过next()函数获取generator中下一个元素

    >>> 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 "<pyshell#24>", line 1, in <module>
        next(G)
    StopIteration

    generator保存的是计算方法,每次调用next(G)就计算出G的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。 因此,在使用next函数获取generator中下一个元素,可以通过捕获异常来判断是否已经取到最后一个元素。

    try:
        next(G)
    except StopIteration:
      /* something hint is the last element */
    pass

    由于generator是可迭代对象,我们也可以通过for循环去获取generator中每一个元素

    >>> G = (x * x for x in range(10))
    >>> for n in G:
        print(n)
    
        
    0
    1
    4
    9
    16
    25
    36
    49
    64
    81

    可以看出,通过for循环来迭代生成器对象,并不用关心StopIteration错误,使用for循环来迭代要比使用next函数要简单的多。

    generator功能十分强大,也可以将一个函数变成生成器。如果,函数中包含yield关键字,那么这个函数就不是一个普通的函数,而是一个generator。

    def sqr_calc(num):
        i = 1
        while num > 0:
            result = i**2
            yield result
            i = i + 1
            num = num -1
        return 'done'

    这是generator的另一种写法, sqr_calc函数中包含yield关键字,这个函数就变成了一个generator,sqr_calc函数中包含了相关的元素的计算方法

    >>> itr = sqr_calc(10)
    >>> print(itr)
    <generator object sqr_calc at 0x00000000051F1648>
    >>> for n in itr:
         print(n)
    
         
    1
    4
    9
    16
    25
    36
    49
    64
    81
    100

    调用sqr_calc函数,返回一个生成器对象,使用for循环,可以依次获取可迭代对象的下一个计算结果

    3、生成器的原理

    函数中有yield关键字,这个函数就变成了一个generator,它的执行流程和函数的执行流程就不同了。函数是顺序执行的,函数遇到return或执行到最后一句语句就返回。而变成generator的函数,在每次调用next函数的时候执行,遇到yield关键字时返回,再次执行时从yield后面的语句继续执行。

    举例如下:

    定义一个gene_test函数,函数中包含多个yield,可以看出改函数是一个generator

    def gene_test():
        print('step 1')
        yield 1
        print('step 2')
        yield 2
        print('step 3')
        yield 3

    调用该gene_test时,首先要生成一个generator对象,然后用next()函数不断获取返回值 

    >>> gen = gene_test()
    >>> next(gen)
    step 1
    1
    >>> next(gen)
    step 2
    2
    >>> next(gen)
    step 3
    3
    >>> next(gen)
    Traceback (most recent call last):
      File "<pyshell#13>", line 1, in <module>
        next(gen)
    StopIteration

    同样的,把函数改成generator后,我们基本上从来不会用next()函数来获取下一个返回值,而是直接使用for循环来迭代。

  • 相关阅读:
    Linux操作篇之配置Samba
    Chrome扩展实现网页图片右键上传(以E站图片搜索为例)
    Linux开机自动挂载NFS配置的一个误区
    ffmpeg指令解读海康威视摄像头
    linux服务器性能调优之tcp/ip性能调优
    多线程程序设计中的8条简单原则
    初识文件系统
    socket中的listen到底干了哪些事情?
    ip面向无连接?TCP面向连接?HTTP连接方式?
    网络层和数据链层的区别
  • 原文地址:https://www.cnblogs.com/053179hu/p/14172287.html
Copyright © 2020-2023  润新知