【python实用特性】- yield生成器


这个概念较难理解,我们可以通过解答以下几个问题来掌握它。
本节会用到可迭代对象列表生成器知识,建议先看下之前的文章进行了解。
【python实用特性】- 迭代、可迭代对象、迭代器
【python实用特性】- 列表生成式

1、什么是生成器?

生成器: 生成,很好理解,创建一个对象的意思。那是什么呢?器可以理解为工具的意思,所以生成器,就是按照一定规则,创建某种对象的工具。

2、为何要用生成器?有何好处?

举个简单的例子,我们要创建一个100个元素的列表。很简单,list(range(100)),那如果要创建一个100万、1000万元素的列表呢?
诚然,我们依旧可以使用list(range(10000*100)list(range(10000*1000)来达到目的。
但这样会带来几个问题:

  • 变量存储于内存中,所以列表长度受限于内存大小,是有限的
  • 列表需要将所有元素一次性创建完成后才能操作,数据过大时,内存使用会瞬间升幅,存在内存不足风险
  • 大多数时候只需要用到部分元素,这就造成了内存空间的浪费
    为了解决以上问题,于是引入了一种不用一次性全部生成,可以按需逐次获取数据的方案——生成器
    所以使用生成器的好处也很明显:
    (1)不用一次性生成所有数据,能有效节约内存
    (2)可以按需逐次获取数据,不会造成内存空间的浪费
3、python中如何实现生成器?

一般来说,python中的生成器有两种实现方式。

  • 普通实现
    将列表生成式的[]改为()即可,实例如下:

    g = (x for x in range(10))
    print(type(g)) #type查看g的类型
    from typing import Iterable
    print(isinstance(g,Iterable)) #判断是否是可迭代对象
    

    输出:

    <class 'generator'>
    True
    

    可见,此时的g便是一个生成器,同时也是一个可迭代对象!
    这意味着我们可以使用 next(g)进行 取值

  • yield实现
    实例如下,打印0~2

    #自然数生成器
    def getN(N):
        n = 0
        while n<N:
            yield n  #使用yield 返回数据
            n+=1
    g = getN(3)
    print(next(g))
    print(next(g))
    print(next(g))
    

    输出:

    0
    1
    2
    

    如上,我们在函数 getN(N)中使用了yield关键字返回数据,所以这个函数就变成了一个生成器。
    小结: 若一个函数中使用了yield返回数据,那这个函数就是一个生成器。

4、yield生成器的运行机制

通过上面的学习,我们已经知道如何去构造一个生成器。那它是如何运行的呢?又为何说它是逐次获取数据呢?
先看一个例子

def test():
    print('返回第一个数据:')
    yield 'data1'
    print('返回第二个数据:')
    yield 'data2'
    print('返回第三个数据:')
    yield 'data3'
g = test()
print(next(g))

输出:

返回第一个数据:
data1

可见,有三个yield 返回,但因为我们只使用了一次 next()函数,所以只打印了data1,第一个yield返回的数据。
那调用两次呢?

print(next(g))
print(next(g))

输出:

返回第一个数据:
data1
返回第二个数据:
data2

小结: 调用一次 next()函数,便执行一次生成器,遇到yield关键字后,将数据返回。之后生成器暂停运行,保存当前的运行状态,等待下一个next()调用。

5、使用循环来迭代生成器

上面的例子中,我们都是通过next()函数来取值。这种方式当数据量大了之后,就会变得很麻烦。所以需要使用更高效的方法,而因为生成器也是一个可迭代对象,所以我们可以使用循环来进行迭代。
打印0~2的实例改写如下:

  • while循环

    
    def getN(N):
        n = 0
        while n<N:
            yield n
            n+=1
    g = getN(10)
    while True:
        try:
            print(next(g),end='\t')
        except StopIteration:
            break #跳出循环
    

    注:使用while循环实现时,要用try...except捕获StopIterator异常。

  • for循环

    def getN(N):
        n = 0
        while n<N:
            yield n
            n+=1
    g = getN(3)
    for data in g:
        print(data)
    

    注:for循环会自动调用next()函数,且捕获异常。推荐使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章