title: python yield && scrapy yield

date: 2020-03-17 16:00:00

categories: python

tags: 语法

yield 关键字用于生成器。 yield在scrapy中的运用。

1 python yield

1.1

参考

https://www.cnblogs.com/chenxi188/p/10848690.html

yield 的作用就是把一个函数变成一个生成器(generator),带有yield的函数不再是一个普通函数,Python解释器会将其视为一个generator,单独调用(如fab(5))不会执行fab函数,而是返回一个 iterable 对象!

在for循环执行时,每次循环都会执行fab函数内部的代码,执行到yield b时,fab函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。参考实例如下:

  1. def fab(max):
  2. n, a, b = 0, 0, 1
  3. while n < max:
  4. yield b
  5. a, b = b, a + b
  6. n = n + 1
  7. print(fab(5)) # 输出:<generator object fab at 0x00000000069D8A68> 说明是个生成器
  8. for n in fab(5):
  9. print n # 依次1,1,2,3,5
  10. #对于含有yield的函数,外部要以迭代的方式调用,当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。
  11. # 在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。

理解上面的代码:加入一些输入输出.

根据下面的输出顺序可知,for循环调用fab这个生成器,fab中while循环开始。

fab中的循环运行一直到yield b,然后返回b,返回到for循环,for循环一次结束后,又返回到生成器(fab)刚刚中断的地方(yield之后)继续运行。一直运行到while一次循环结束,下一次循环碰到yield b,再次返回

见输出中

globalll n3 1

globalll n4 1

  1. def fab(max):
  2. i, a, b = 0, 0, 1
  3. while i < max:
  4. print("globalll n4 %d" % (globalll))
  5. print("b %d i %d" % (b, i))
  6. yield b
  7. a, b = b, a + b
  8. print("a %d b %d i %d" % (a,b, i))
  9. i = i + 1
  10. print("globalll n3 %d"%(globalll))
  11. globalll=0
  12. for n in fab(5):
  13. print("globalll n1 %d"%(globalll))
  14. print(n)
  15. globalll=globalll+1
  16. print("globalll n2 %d" % (globalll))
  17. globalll n4 0
  18. b 1 i 0
  19. globalll n1 0
  20. 1
  21. globalll n2 1
  22. a 1 b 1 i 0
  23. globalll n3 1
  24. globalll n4 1
  25. b 1 i 1
  26. globalll n1 1
  27. 1
  28. globalll n2 2
  29. a 1 b 2 i 1
  30. globalll n3 2
  31. globalll n4 2
  32. b 2 i 2
  33. globalll n1 2
  34. 2
  35. globalll n2 3
  36. a 2 b 3 i 2
  37. globalll n3 3
  38. globalll n4 3
  39. b 3 i 3
  40. globalll n1 3
  41. 3
  42. globalll n2 4
  43. a 3 b 5 i 3
  44. globalll n3 4
  45. globalll n4 4
  46. b 5 i 4
  47. globalll n1 4
  48. 5
  49. globalll n2 5
  50. a 5 b 8 i 4
  51. globalll n3 5
  1. def ff(max):
  2. a,b = 0,1
  3. yield max # yield不在循环中,这里已经到函数最后所以直接返回,相当于return
  4. for n in ff(5):
  5. print n # 输出:5

1.2 yield 迭代对象 生成器

参考了

https://pyzh.readthedocs.io/en/latest/the-python-yield-keyword-explained.html

生成器的优势是,数据不是存在内存中,只能读一次(读的时候生成)

可迭代对象

当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象:

  1. >>> mylist = [1, 2, 3]
  2. >>> for i in mylist :
  3. ... print(i)
  4. 1
  5. 2
  6. 3

mylist 是一个可迭代的对象。当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代的对象:

  1. >>> mylist = [x*x for x in range(3)]
  2. >>> for i in mylist :
  3. ... print(i)
  4. 0
  5. 1
  6. 4

所有你可以使用 for .. in .. 语法的叫做一个迭代器:列表,字符串,文件……你经常使用它们是因为你可以如你所愿的读取其中的元素,但是你把所有的值都存储到了内存中,如果你有大量数据的话这个方式并不是你想要的。

生成器

生成器是可以迭代的,但是你 只可以读取它一次 ,因为它并不把所有的值放在内存中,它是实时地生成数据:

mygenerator = (x*x for x in range(3))

for i in mygenerator :

... print(i)

0

1

4

看起来除了把 [] 换成 () 外没什么不同。但是,你不可以再次使用 for i in mygenerator , 因为生成器只能被迭代一次:先计算出0,然后继续计算1,然后计算4,一个跟一个的…

yield关键字

yield 是一个类似 return 的关键字,只是这个函数返回的是个生成器。

  1. >>> def createGenerator() :
  2. ... mylist = range(3)
  3. ... for i in mylist :
  4. ... yield i*i
  5. ...
  6. >>> mygenerator = createGenerator() # create a generator
  7. >>> print(mygenerator) # mygenerator is an object!
  8. <generator object createGenerator at 0xb7555c34>
  9. >>> for i in mygenerator:
  10. ... print(i)
  11. 0
  12. 1
  13. 4

这个例子没什么用途,但是它让你知道,这个函数会返回一大批你只需要读一次的值.

为了精通 yield ,你必须要理解:当你调用这个函数的时候,函数内部的代码并不立马执行 ,这个函数只是返回一个生成器对象,这有点蹊跷不是吗。

那么,函数内的代码什么时候执行呢?当你使用for进行迭代的时候.

现在到了关键点了!

第一次迭代中你的函数会执行,从开始到达 yield 关键字,然后返回 yield 后的值作为第一次迭代的返回值. 然后,每次执行这个函数都会继续执行你在函数内部定义的那个循环的下一次,再返回那个值,直到没有可以返回的。

如果生成器内部没有定义 yield 关键字,那么这个生成器被认为成空的。这种情况可能因为是循环进行没了,或者是没有满足 if/else 条件。

2 yield 与 scrapy

参考

https://www.cnblogs.com/chenxi188/p/10848690.html

https://www.oschina.net/question/2254016_238539

https://www.zhihu.com/question/30201428

scrapy中的yield还是生成器。

通过 yield 来发起一个请求,并通过 callback 参数为这个请求添加回调函数,在请求完成之后会将响应作为参数传递给回调函数。 scrapy框架会根据 yield 返回的实例类型来执行不同的操作:

a. 如果是 scrapy.Request 对象,scrapy框架会去获得该对象指向的链接并在请求完成后调用该对象的回调函数。

b. 如果是 scrapy.Item 对象,scrapy框架会将这个对象传递给 pipelines.py做进一步处理。

先说scrapy的spider:

Scrapy为Spider的 start_urls 属性中的每个URL创建了 scrapy.Request 对象,并将 parse 方法作为回调函数(callback)赋值给了Request。

Request对象经过调度,执行生成 scrapy.http.Response 对象并送回给spider parse() 方法。

比如下面,其中parse为默认的方法。

  1. class DmozSpider(scrapy.Spider):
  2. name = "dmoz"
  3. allowed_domains = ["dmoz.org"]
  4. start_urls = [
  5. def parse(self, response):

当parse中使用了yield,parse就会被当成一个生成器。

  1. 1. 因为使用的yield,而不是returnparse函数将会被当做一个生成器使用。scrapy会逐一获取parse方法中生成的结果,并判断该结果是一个什么样的类型;
  2. 2. 如果是request则加入爬取队列,如果是item类型则使用pipeline处理,其他类型则返回错误信息。
  3. 3. scrapy取到第一部分的request不会立马就去发送这个request,只是把这个request放到队列里,然后接着从生成器里获取;
  4. 4. 取尽第一部分的request,然后再获取第二部分的item,取到item了,就会放到对应的pipeline里处理;
  5. 5. parse()方法作为回调函数(callback)赋值给了Request,指定parse()方法来处理这些请求 scrapy.Request(url, callback=self.parse)
  6. 6. Request对象经过调度,执行生成 scrapy.http.response()的响应对象,并送回给parse()方法,直到调度器中没有Request(递归的思路)
  7. 7. 取尽之后,parse()工作结束,引擎再根据队列和pipelines中的内容去执行相应的操作;
  8. 8. 程序在取得各个页面的items前,会先处理完之前所有的request队列里的请求,然后再提取items
  9. 9. 这一切的一切,Scrapy引擎和调度器将负责到底。

当parse中有多个yield就可以同时获得页面的内容和url。比如下面的代码,先是yield"返回"了scrapy.Request,返回后就调用scrapy.Request,结束后,继续执行最开始的yield item 返回了网页内容的生成器给pipeline。

暂时的理解是这样做生成了很多生成器而不是数据直接存在内存中,最后通过item生成器把数据给到pipeline,比较适合爬虫的大量数据。(直接递归就会有大量数据在内存)

  1. def parse(self, response):
  2. #do something
  3. yield scrapy.Request(url, callback=self.parse)
  4. #item[key] = value
  5. yield item

不用yield写一下parse就可以理解。

  1. def parse(self, response):
  2. result_list = []
  3. for h3 in response.xpath("//h3").extract():
  4. result_list.append(MyItem(title=h3)
  5. for url in response.xpath("//a/@href").extract():
  6. result_list.append(scrapy.Request(url, callback=self.parse))
  7. return result_list

区别在于用了yield的函数会返回一个生成器,生成器不会一次把所有值全部返回给你,而是你每调用一次next返回一个值。for已经调用了next()

next()函数作用可以在网上查

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。

3其他

上面是一些简单的了解,还想继续了解可以查看

https://docs.python.org/3/reference/expressions.html#yieldexpr

建议阅读python官方tutorial的class那章关于iterators和generators的那几节

https://www.jianshu.com/p/d09778f4e055

python yield && scrapy yield的更多相关文章

  1. 关于Python中的yield

    关于Python中的yield   在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,f ...

  2. (转) Python Generators(生成器)——yield关键字

    http://blog.csdn.net/scelong/article/details/6969276 生成器是这样一个函数,它记住上一次返回时在函数体中的位置.对生成器函数的第二次(或第 n 次) ...

  3. scrapy yield Request

    import scrapy from myproject.items import MyItem class MySpider(scrapy.Spider): name = ’example.com’ ...

  4. Python生成器与yield

    列表推导与生成器表达式 当我们创建了一个列表的时候,就创建了一个可以迭代的对象: >>> squares=[n*n for n in range(3)] >>> f ...

  5. 【Python学习】yield send我就说这么多

    C#的yield已经忘得差不多了.又遇到python的yield.iterator def testYield(): print 'yield1' m = yield 1 print 'm =' , ...

  6. python yield 与 yield from转

    python yield 与 yield from转 https://blog.csdn.net/chenbin520/article/details/78111399?locationNum=7&a ...

  7. 从yield 到yield from再到python协程

    yield 关键字 def fib(): a, b = 0, 1 while 1: yield b a, b = b, a+b yield 是在:PEP 255 -- Simple Generator ...

  8. 深入理解Python中的yield和send

    send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互. 但是需要注意,在一个生成器对象没有执行next方法之前,由 ...

  9. Python:笔记(7)——yield关键字

    Python:笔记(7)——yield关键字 yield与生成器 所谓生成器是一个函数,它可以生成一个值的序列,以便在迭代中使用.函数使用yield关键字可以定义生成器对象. 一个例子 我们调用该函数 ...

随机推荐

  1. 一文读懂 Kubernetes APIServer 原理

    前言 整个Kubernetes技术体系由声明式API以及Controller构成,而kube-apiserver是Kubernetes的声明式api server,并为其它组件交互提供了桥梁.因此加深 ...

  2. ELK一个优秀的日志收集、搜索、分析的解决方案

    1 什么是ELK? ELK,是Elastaicsearch.Logstash和Kibana三款软件的简称.Elastaicsearch是一个开源的全文搜索引擎.Logstash则是一个开源的数据收集引 ...

  3. 如何封装Promise对象?

    最近看到了一个有趣的Promise的方法,这里记录下来 <script> class MyPromise { constructor(executor) { // 初始化state赋值为p ...

  4. nfs samba文件共享服务

    (注意:实验之前强关闭selinux和防火墙) 一丶nfs ① 1.服务端 启动服务 systemctl start nfs.service   配置文件 vim /etc/exports share ...

  5. 在QML 中用javascritpt 将中文转换拼音,可以在音标

    项目需要, 今天整理了一下.在QML调用javascrit将中文汉字转换成拼音. 感觉执行效率低.下面是主要代码. 具体代码请参考QMLPinyin 代码 ```import "./piny ...

  6. VirtualBox Guest Additions 下载地址以及简介

    下载者可将以下链接粘贴到浏览器上,根据Vbox的版本找到自己对应的增强. http://download.virtualbox.org/virtualbox/5.0.10/ 虚拟机安装VBoxAddi ...

  7. 3、剑指offer-数组——数组中重复的数字

    *题目描述* **在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输 ...

  8. 手淘架构组最新实践 | iOS基于静态库插桩的⼆进制重排启动优化 抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15% 编译期插桩

    抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15% 原创 Leo 字节跳动技术团队 2019-08-09 https://mp.weixin.qq.com/s/Drmmx5JtjG ...

  9. Connection Pool

    MySQL :: MySQL Connector/NET Developer Guide :: 4.3 Managing a Connection Pool in Connector/NET http ...

  10. (Oracle)取当前日期的最近工作日

      描述:现有一需求,日期表中存放了日期和是否节假日(0-工作日,1-节假日),现在需要取日期表中的最近的工作日.如2017/07/23(周日)最近的工作日应该是2017/07/21(周五).     ...