yield作用
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator。下面以斐波拉契数列来做个说明:
# 普通的函数实现 def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 return 'done' """不足: 在 fab 函数中用 print 打印数字会导致该函数可复用性较差 因为其他函数无法获得该函数生成的数列。 """ # 返回一个 List def fib(max): n, a, b = 0, 0, 1 L = [] while n < max: L.append(b) a, b = b, a + b n = n + 1 return L """不足 该函数在运行中占用的内存会随着参数 max 的增大而增大,如果要控制内存占用,最好不要用 List 来保存中间结果,而是通过iterable对象来迭代。 """ # 使用yield def fab(max): n, a, b = 0, 0, 1 while n < max: yield b # print b a, b = b, a + b n = n + 1 """ 带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,
调用 fab(5) 不会执行fab函数,而是返回一个 iterable对象 """
下面执行yield版的fab函数:
# 运行 for n in fab(5): print(n) # 结果 1 1 2 3 5
在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。
yield send方法
关于生成器中的send方法,之前没理解,只知道有一个next方法。通过下面代码实例,记录一下自己的理解:
def func1(): # 生成器函数 x = yield 1 print('This is x in func1: ', x) x = yield x # print('This is x:', x) f1 = func1() print('This is next(f1): ', next(f1)) # 当调用next(f1)方法时,python首先会执行func1方法的yield 1语句 # next方法返回值为yield关键字后面表达式的值,即为1 # 此时方法func1中断执行,x的赋值以及之后的语句,不再执行 # 执行send方法或next方法: print("This is f1.send('e'):", f1.send('e')) # print("Second next:", next(f1)) # 当调用f1.send('e')方法时,恢复之前yield引起的中断 # 继续执行 x = yield 1,对x赋值 # 此时(yield 1)的返回值是send方法的参数值'e',并将其赋值给x # 然后继续执行 print('This is x in func1: ', x) # 执行结果是This is x in func1: e # 当调用next方法—>print("Second next:", next(f1)) # func1会从之前的中断—>yield 1语句,继续运行 # 语句 print('This is x in func1: ', x) 将被执行。 # 但是,此时yield 1的返回值是None,并将其赋值给x # 因此,执行结果是:This is x in func1: None # 如果调用f1.send('e')方法,则接下来继续执行,会遇到yield x语句 # func1再次被挂起。 # 此时,send方法的返回值为yield关键字后面表达式的值,即是x的值'e' # 执行结果是:This is f1.send('e'): e # 如果func1方法内无 yield x 语句,将会报错:StopIteration # 如果此时再次调用send方法 # print("This is f1.send('f'):", f1.send('f')) # 此时表达式(yield x)的返回值定义为send方法参数的值,即为'f' # 接下来x = yield x 这一赋值语句会将x的值置为'f' # 继续运行,func1方法执行完毕,抛出StopIteration异常 # f1.send('f')也就没有返回值了。 # 但是,我们可以在func1方法,x = yield x语句后,中加入一条语句 # print('This is last x: ', x),用来测试x的值‘f’
总之,send方法和next方法的区别在于,执行send方法时,会首先把send方法内的参数赋值给上一次挂起的yield语句的返回值。但是需要注意,在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以执行send方法会报错:
TypeError: can't send non-None value to a just-started generator
但是,下面的语句是可行的:
f2 = func1() print("Send None value: ", f3.send(None)) # 运行结果
# Send None value: 1
当send方法的参数为None时,它与next方法完全等价。但是注意,虽然上面的代码可以接受,但是不规范。所以,在调用send方法之前,还是先调用一次next方法为好。
以上,就是我通过查找资料,搞清楚yield 中send方法的过程与心得。感谢:https://blog.csdn.net/hedan2013/article/details/56293173 提供的参考。