yield一般多用于生成器的创建,通过next()和send方法进行调用。
def demo01():
print('start! 第1次循环')
for i in range(10):
c = yield i
print('c ---->',c,'\n')
print('!!!!end!!!! 第%d次循环'%(i+1))
if __name__ == '__main__':
d = demo01()
print('=========')
print('第一次打印返回值:',next(d))
print('=========')
print('第二次打印返回值:',d.send(11))
print('=========')
print('第3次打印返回值:',next(d))
结果:
=========
!!!!start!!!! 第1次循环
第一次打印返回值: 0
=========
c ----> 11
!!!!end!!!! 第1次循环
第二次打印返回值: 1
=========
c ----> None
!!!!end!!!! 第2次循环
第3次打印返回值: 2
如果把yield看作是一个return,那么执行到yield的时候就是返回指定的数据,然后跳出函数。
综上所述可以看出,两种调用方法,都是从上一步结束的地方继续执行,next()不会传入参数,而send可以向函数内传入参数,通过yield的位置传参。
def demo02():
for i in range(10):
c = yield i
print('c1 ---->',c)
def demo03():
for i in range(10):
yield i
c = i
print('c2 ----->',c)
d2 = demo02()
d3 = demo03()
next(d2)
next(d3)
d2.send(11)
d3.send(11)
===结果===
c1 ----> 11
c2 -----> 0
demo03中虽然依旧使用了send传参,但是继续执行上一步终止处的程序后,新传入的参数并没有被赋值,因此传入的参数无效。
TypeError: can't send non-None value to a just-started generator
第一次调用生成器时,只能用send(None)或next()方法,不要直接send赋值,不然会报这个错误。
官方文档里详细解释了这个异常。是因为当生成器创建,从顶部开始执行函数时,并没有可以接收yield值的表达式,所以不可以使用带有非None参数的send(),必须提前调用一次send(None)或next()方法。
官方说明如下:
Because generator-iterators begin execution at the top of the
generator’s function body, there is no yield expression to receive a value when the generator has just been created. Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started, and a TypeError is raised if this occurs (presumably due to a logic error of some kind). Thus, before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield
StopIteration
每一次调用next()方法后都会执行到yield位置结束,然后返回值,如果程序已经执行到最低端无法继续向下执行,此时仍然调用了方法,就会抛出这个异常。
在scrapy中,关于yield最常见的两个操作就是yield scrapy.Request
和yield scrapy.item
scrapy.Request
def parse(self,response):
···
yield scrapy.Request(url=url,callback=self.detail_parse)
向response中获取的新链接再次发起请求,请求完成后调用指定回调函数。
scrapy.item
def detail_parse(self,response):
item = DemoItem()
····
yield item
通过yield将获取的item传输到管道文件进行下一步处理。
如果在scrapy中定义了一个非回调函数,那么在这个函数中无法进行yield scrapy.Request
和yield scrapy.item
的操作,此时yield在这里起到的是它本身的基础作用,不是scrapy赋予它的作用,而使用了yield的非回调函数也只是一个普通的生成器。