yield的使用和在scrapy框架中的使用

yield的基本使用

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的时候就是返回指定的数据,然后跳出函数。

  • 此时 d 就是一个生成器,由于函数没有被执行过,执行第一个next时,函数正式被运行,做第一步的print输出,然后进入for循环,遇到yield后返回指定 i 值并跳出函数执行,此时 i =0,所以第一次打印的返回值为0
  • 第二步使用了send()函数,此时从赋值的那一步开始执行(即通过yield跳出的那一步)并将传入的值赋值给了c,此时c = 11,然后继续向下执行,结束第一次循环,再次循环遇到yield时,返回的 i 值为1。
  • 第三步再次使用了next方法,依旧从跳出的那一步开始向下执行,结束第2次循环,再次遇到yield时,返回 i 值,此时 i = 2。
    • 但是,在结束第2次循环之前,在打印c的时候显示c = None。这是因为在第二步使用send函数跳出后,c并没有被赋值,在执行左边的yield时,就已经跳出了所以并没有执行复制的操作,所以这是默认赋值为None,所以在进行第三步时,c为None。

综上所述可以看出,两种调用方法,都是从上一步结束的地方继续执行,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传参,但是继续执行上一步终止处的程序后,新传入的参数并没有被赋值,因此传入的参数无效。

常见yield的异常

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的使用

在scrapy中,关于yield最常见的两个操作就是yield scrapy.Requestyield 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中定义了一个非回调函数,那么在这个函数中无法进行yield scrapy.Requestyield scrapy.item的操作,此时yield在这里起到的是它本身的基础作用,不是scrapy赋予它的作用,而使用了yield的非回调函数也只是一个普通的生成器。

你可能感兴趣的:(python爬虫,python,scrapy,python)