正在加载...

tornado-11 谈yield, generator

上篇埋了一个坑,本篇先填一点点。

对于python中的yield, 大家应该都知道。

# -*- coding:utf-8 -*-

def foo(n):
    for i in range(n):
        yield i

for x in foo(2):
    print x

上面的代码输出的就是1,2。注意上面的代码,可以看到foo(1)其实返回的就是一个generator。 可以理解为一旦遇到yield的语句,就会返回一个generator。看看官方的解释:

Any function containing a yield keyword is a generator function;

从上面的基本代码来看,我们可以简单的理解为:遇到yield i, 就返回i,并且产生函数的临时中断,控制权交给外面的代码。等外面的代码消耗了这个返回值后,控制权在交回给函数,从之前的临时中断处再接着执行。

当然,上述的理解并不完全正确。我们再来看看下面的代码。

# -*- coding:utf-8 -*-

def foo(n):
    for i in range(n):
        yield i

g = foo(2)      # 1
print g.next()  # 2
print g.next()  # 3
print g.next()  # 4

上面的代码输出的是:

0
1
Traceback (most recent call last):
  File "/Users/ryan/Workspace/myself/Tornadjango/app/tmp.py", line 10, in <module>
    print g.next()
StopIteration

现在明白了么?因为foo方法里面有yield,所以调用它总是会返回一个generator。所以

1 得到一个generator,且在1的时候,foo函数不会有任何的执行。在解释器分析代码的时候,发现foo里面有yield,就已经把它当成一个generator了。

2 得到generator的第一个值 0

3 得到generator的第二个值 1

4 由于generator已经完毕,所以,抛出StopIteration异常

再来看下面的代码:

# -*- coding:utf-8 -*-

def foo(n):
    for i in range(n):
        v = yield i
        print 'v_%s = %s' % (i, v) # 5

g = foo(2)       # 1
print g.next()   # 2
print g.next()   # 3
print g.next()   # 4

这个时候,结果是:

0
v_0 = None
1
v_1 = None
Traceback (most recent call last):
  File "/Users/ryan/Workspace/myself/Tornadjango/app/tmp.py", line 11, in <module>
    print g.next()
StopIteration

发现了么? v的值是None哦。如果你是一个初学者,你肯定会认为 v 的值应该等于i。但是其实不是的。那么v为什么等于None呢 ?

再看下面的代码:

# -*- coding:utf-8 -*-

def foo(n):
    for i in range(n):
        v = yield i
        print 'v_%s = %s' % (i, v)

g = foo(2)
print g.next()
print g.send('a')

结果如下:

0
v_0 = a
1

可以看出。g.send('a')的时候,把'a'的值付给了v。

那我们这样写呢?

# -*- coding:utf-8 -*-

def foo(n):
    for i in range(n):
        v = yield i
        print 'v_%s = %s' % (i, v)

g = foo(2)
print g.send('a')

出现了如下的错误:

Traceback (most recent call last):
  File "/Users/ryan/Workspace/myself/Tornadjango/app/tmp.py", line 9, in <module>
    print g.send('a')
TypeError: can't send non-None value to a just-started generator

所以得出如下的结论:

  • generator.next() 等价于 generator.send(None)

  • 对于一个generator,一定要先调用一个next,或者send(None)。才能再调用send(value)

  • 调用send(value)的时候,会把value的值赋给里面的接收者,且是上一次next的中断位置