Python yield 使用浅析

https://www.runoob.com/w3cnote/python-yield-used-analysis.html

1、英文意思

v.出产(作物);产生(收益、效益等);提供;屈服;让步;放弃;缴出 n.产量;产出;利润

2、理解yield

带有 yield 的函数在 Python 中被称之为 generator(生成器)。 如果要控制内存占用,最好不要用List来保存中间结果,而是通过 iterable 对象来迭代。

for i in range(1000): pass会导致生成一个 1000 个元素的List,而代码:for i in xrange(1000): pass 则不会生成一个 1000 个元素的List,而是在每次迭代中返回下一个数值,内存空间占用很小。因为 xrange 不返回 List,而是返回一个 iterable 对象。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class Fab(object): 
    def __init__(self, max): 
        self.max = max 
        self.n, self.a, self.b = 0, 0, 1 

    def __iter__(self): 
        return self 

    def next(self): 
        if self.n < self.max: 
            r = self.b 
            self.a, self.b = self.b, self.a + self.b 
            self.n = self.n + 1 
            return r 
        raise StopIteration()

for n in Fab(5): 
    print(n)

上面的类可以理解为yield的实现。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b      # 使用 yield
        # print(b) 
        a, b = b, a + b 
        n = n + 1

for n in fab(5): 
    print(n)

f = fab(5)
while True:
    try:
        n = next(f)
        print(n)
    except Exception as ex:
        break

使用next函数需要注意会有抛出异常。

yield无法知道genertor中有多少元素,需要用到的时候调用一次函数,因此无法和进度条结合使用。

3、再次理解

可以理解yield相当于是一个return。

迭代器有终止的时候,因此需要抛出异常。

4、Python函数式编程——生成器(generator)

4-1、创建生成器

列表受到内存限制,列表容量肯定是有限的。 并且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前1万个元素,那后面绝大多数元素占用的空间都被浪费了。 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?

创建 列表 和 生成器 的区别仅在于最外层的 [ ] 和 ( ) , lst 是一个列表,而g 是一个生成器。我们可以直接打印出 lst 的每一个元素,但我们怎么打印出 g 的每一个元素呢?如果要把每个打印出来,可以通过 next() 函数获得生成器的下个返回值。

详情见:D:\Github\Storage\python\study\生成器generator\generator.py

也可以通过循环打印出来。

有两种方法创建: 1、通过列表生成式来创建,局限性:数据有限,无法写一些复杂的代码 2、通过函数来创建生成器。(yield)

4-2、迭代器中的StopIteration

什么是迭代器?它是一个带状态的对象,在你调用next()方法的时候返回容器中的下一个值,任何实现了iternext()(python2中实现next())方法的对象都是迭代器,iter返回迭代器自身,next返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。可迭代对象实现了iter方法,该方法返回一个迭代器对象。

生成器保存的是算法,每次调用next(g) ,就计算出 g 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的异常。当然,这种不断调用next() 实在是太繁琐了,虽然是点一次出现一次,但正确的做法是使用 for 循环,因为生成器也是可迭代对象。所以,我们创建了一个生成器后,基本上永远不会调用next() ,而是通过 for 循环来迭代它,并且不需要关心StopIteration 异常。

4-3、遍历生成器中元素的内容

1、通过next(g),可能抛异常,StopIteration 2、通过for循环来遍历 3、通过object对象内置的next来遍历,当生成器中没有数据时,也会抛出异常StopIteration 4、调用内置的send函数 #必须传参数,且第一个值必须传空值,后面的值就没有限制了。

5、用列表推导来取代map和filter

列表推导(list commprehension):根据一份列表来制作另外一份列表。

results matching ""

    No results matching ""