【101】Python Generator漫谈(续)



  • 上篇介绍了generator相关的概念. 这篇来探究一下generator相关的操作方法. 以下都是使用Python 2.7.10.

    根据官方文档 generator 通过 yield 来实现 next() 方法.

    A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

    》》》 def fib():
    … a, b = 0, 1
    … while True:
    … yield b
    … a, b = b, a + b

    》》》 fib
    <function fib at 0x10d930c80>
    》》》f = fib()
    》》》 f
    <generator object fib at 0x10d92faa0>
    》》》next(f)
    1
    》》》 next(f)
    1
    》》》 next(f)
    2
    》》》next(f)
    3
    》》 》next(f)
    5

    以上, 我们可以看到yield用法. 每次执行到yield, 都会记住执行状态的局部变量以及表达式。再下次恢复时, 即next(), 它会从记住的状态继续执行.

    generator的另一种调用方法是通过send(value)来实现的。我们来看一个例子:

    》》def gen():
    … while True:
    … value = yield
    … print(value)

    》》》g = gen()
    》》》g.send(“ek”)
    Traceback (most recent call last):
    File “”, line 1, in
    TypeError: can’t send non-None value to a just-started generator

    》》》 next(g)
    》》》 g.send(“ek”)
    ek
    》》》 next(g)
    None

    以上, 展示了如何讲value传入yield当前表达式. 具体来说, 使用send(value)时, generator停在yield的语句. 传入的值被复制到value, 然后print函数打印value, 经过循环遇到yield时, 暂停. 需要注意的是在没有执行next()前, generator状态并没有停在yield状态,也就无法传入值.

    》》》g = gen()
    》》》 g.send(None)
    》》》 g.send(“ek”)
    ek

    以上, 除了next(), 可以让generator到达yield, send(None)作用是一样的.

    让我们乘热打铁, 看一个稍微复杂的例子:

    》》》 def gen(value = None):
    … while True:
    … value = (yield value)
    … print(“The value is”, value)
    … if value:
    … value += 1

    》》》g = gen(1)
    》》》next(g)
    1
    》》》g.send(2)
    (‘The value is’, 2)
    3
    》》》 g.send(10)
    (‘The value is’, 10)
    11
    》》》 next(g)
    (‘The value is’, None)

    在这个例子里, value = (yield value) 这个形式看起来很复杂. 但两个value 的含义并不相同. 具体来看, 执行next()时, generator执行到yield value表达式, 保存上下文环境暂停返回当前值1. 再执行send(value)时, 从value = yield开始, 打印传入的值 2, 再次遇到yield value暂停返回当前值 3 (上个循环时 加上 1). send(10)是一样的. 在看最后一个next(), 这里需要记住的是调用next()表达式的值时, yield的值总是为None. 因此, 在这里返回None.

    接下来, 我们来实现一下Python 内建函数 range() 实现方法 (复刻版, 非真实实现):

    》》》 def my_range(start, stop, step = 1):
    … if stop <= start:
    … raise RuntimeError(“Start must be smaller than stop”)
    … i = start
    … while i < stop:
    … yield i
    … i += step

    》》》 try:
    … for k in my_range(10, 50, 3):
    … print(k)
    … except RuntimeError as ex:
    … print(ex)
    … except:
    … print(“Unknown error occurred”)

    10
    13
    16
    19
    22
    25
    28
    31
    34
    37
    40
    43
    46
    49

    最后, generator 的另外两个方法分别是 throw(type[, value[, traceback]]) 和 close(). 前者用于抛出 type 异常, 后者用于关闭generator. 我们直接看下Python文档的例子:

    》》》 def echo(value=None):
    … print(“Execution starts when ‘next()’ is called for the first time.”)
    … try:
    … while True:
    … try:
    … value = (yield value)
    … except Exception as e:
    … value = e
    … finally:
    … print(“Don’t forget to clean up when ‘close()’ is called.”)

    》》》 generator = echo(1)
    》》》 print(next(generator))
    Execution starts when ‘next()’ is called for the first time.
    1
    》》》 print(next(generator))
    None
    》》》 print(generator.send(2))
    2
    》》》 generator.throw(TypeError, “spam”)
    TypeError(‘spam’,)
    》》》 generator.close()
    Don’t forget to clean up when ‘close()’ is called.

    欢迎访问我的博客 原文链接

    本文作者:EK


登录后回复
 

与 BitTiger Community 的连接断开,我们正在尝试重连,请耐心等待