python yield && scrapy yield
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。参考实例如下:
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
print(fab(5)) # 输出:<generator object fab at 0x00000000069D8A68> 说明是个生成器
for n in fab(5):
print n # 依次1,1,2,3,5
#对于含有yield的函数,外部要以迭代的方式调用,当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。
# 在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。
理解上面的代码:加入一些输入输出.
根据下面的输出顺序可知,for循环调用fab这个生成器,fab中while循环开始。
fab中的循环运行一直到yield b,然后返回b,返回到for循环,for循环一次结束后,又返回到生成器(fab)刚刚中断的地方(yield之后)继续运行。一直运行到while一次循环结束,下一次循环碰到yield b,再次返回
见输出中
globalll n3 1
globalll n4 1
def fab(max):
i, a, b = 0, 0, 1
while i < max:
print("globalll n4 %d" % (globalll))
print("b %d i %d" % (b, i))
yield b
a, b = b, a + b
print("a %d b %d i %d" % (a,b, i))
i = i + 1
print("globalll n3 %d"%(globalll))
globalll=0
for n in fab(5):
print("globalll n1 %d"%(globalll))
print(n)
globalll=globalll+1
print("globalll n2 %d" % (globalll))
globalll n4 0
b 1 i 0
globalll n1 0
1
globalll n2 1
a 1 b 1 i 0
globalll n3 1
globalll n4 1
b 1 i 1
globalll n1 1
1
globalll n2 2
a 1 b 2 i 1
globalll n3 2
globalll n4 2
b 2 i 2
globalll n1 2
2
globalll n2 3
a 2 b 3 i 2
globalll n3 3
globalll n4 3
b 3 i 3
globalll n1 3
3
globalll n2 4
a 3 b 5 i 3
globalll n3 4
globalll n4 4
b 5 i 4
globalll n1 4
5
globalll n2 5
a 5 b 8 i 4
globalll n3 5
def ff(max):
a,b = 0,1
yield max # yield不在循环中,这里已经到函数最后所以直接返回,相当于return
for n in ff(5):
print n # 输出:5
1.2 yield 迭代对象 生成器
参考了
https://pyzh.readthedocs.io/en/latest/the-python-yield-keyword-explained.html
生成器的优势是,数据不是存在内存中,只能读一次(读的时候生成)
可迭代对象
当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象:
>>> mylist = [1, 2, 3]
>>> for i in mylist :
... print(i)
1
2
3
mylist 是一个可迭代的对象。当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代的对象:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist :
... print(i)
0
1
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 的关键字,只是这个函数返回的是个生成器。
>>> def createGenerator() :
... mylist = range(3)
... for i in mylist :
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
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为默认的方法。
class DmozSpider(scrapy.Spider):
name = "dmoz"
allowed_domains = ["dmoz.org"]
start_urls = [
def parse(self, response):
当parse中使用了yield,parse就会被当成一个生成器。
1. 因为使用的yield,而不是return。parse函数将会被当做一个生成器使用。scrapy会逐一获取parse方法中生成的结果,并判断该结果是一个什么样的类型;
2. 如果是request则加入爬取队列,如果是item类型则使用pipeline处理,其他类型则返回错误信息。
3. scrapy取到第一部分的request不会立马就去发送这个request,只是把这个request放到队列里,然后接着从生成器里获取;
4. 取尽第一部分的request,然后再获取第二部分的item,取到item了,就会放到对应的pipeline里处理;
5. parse()方法作为回调函数(callback)赋值给了Request,指定parse()方法来处理这些请求 scrapy.Request(url, callback=self.parse)
6. Request对象经过调度,执行生成 scrapy.http.response()的响应对象,并送回给parse()方法,直到调度器中没有Request(递归的思路)
7. 取尽之后,parse()工作结束,引擎再根据队列和pipelines中的内容去执行相应的操作;
8. 程序在取得各个页面的items前,会先处理完之前所有的request队列里的请求,然后再提取items。
9. 这一切的一切,Scrapy引擎和调度器将负责到底。
当parse中有多个yield就可以同时获得页面的内容和url。比如下面的代码,先是yield"返回"了scrapy.Request,返回后就调用scrapy.Request,结束后,继续执行最开始的yield item 返回了网页内容的生成器给pipeline。
暂时的理解是这样做生成了很多生成器而不是数据直接存在内存中,最后通过item生成器把数据给到pipeline,比较适合爬虫的大量数据。(直接递归就会有大量数据在内存)
def parse(self, response):
#do something
yield scrapy.Request(url, callback=self.parse)
#item[key] = value
yield item
不用yield写一下parse就可以理解。
def parse(self, response):
result_list = []
for h3 in response.xpath("//h3").extract():
result_list.append(MyItem(title=h3)
for url in response.xpath("//a/@href").extract():
result_list.append(scrapy.Request(url, callback=self.parse))
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的更多相关文章
- 关于Python中的yield
关于Python中的yield 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,f ...
- (转) Python Generators(生成器)——yield关键字
http://blog.csdn.net/scelong/article/details/6969276 生成器是这样一个函数,它记住上一次返回时在函数体中的位置.对生成器函数的第二次(或第 n 次) ...
- scrapy yield Request
import scrapy from myproject.items import MyItem class MySpider(scrapy.Spider): name = ’example.com’ ...
- Python生成器与yield
列表推导与生成器表达式 当我们创建了一个列表的时候,就创建了一个可以迭代的对象: >>> squares=[n*n for n in range(3)] >>> f ...
- 【Python学习】yield send我就说这么多
C#的yield已经忘得差不多了.又遇到python的yield.iterator def testYield(): print 'yield1' m = yield 1 print 'm =' , ...
- python yield 与 yield from转
python yield 与 yield from转 https://blog.csdn.net/chenbin520/article/details/78111399?locationNum=7&a ...
- 从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 ...
- 深入理解Python中的yield和send
send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互. 但是需要注意,在一个生成器对象没有执行next方法之前,由 ...
- Python:笔记(7)——yield关键字
Python:笔记(7)——yield关键字 yield与生成器 所谓生成器是一个函数,它可以生成一个值的序列,以便在迭代中使用.函数使用yield关键字可以定义生成器对象. 一个例子 我们调用该函数 ...
随机推荐
- Kubernetes 升级过程记录:从 1.17.0 升级至最新版 1.20.2
本文记录的是将 kubernetes 集群从 1.17.0 升级至最新版 1.20.2 的实际操作步骤,由于 1.17.0 无法直接升级到 1.20.2,需要进行2次过滤升级,1.17.0 -> ...
- VPS下环境漏洞部署
No.1 声明 1.由于本环节运行在公网,如何同样复现情况,复现成功后请立即关闭环境! 2.本环境仅用于漏洞复现! No.2 安装docker curl -s https://get.docker.c ...
- IP2723T中文规格书PDF
IP2723T 是一款集成多种协议.用于 USB 输出端口的快充协议 IC.支持多种快充协议,包括 USBTypeC DFP,PD2.0/PD3.0/PPS,HVDCPQC4/QC4+/QC3.0/Q ...
- consul是什么?
consul概念: consul是用来做注册中心的 他和eureka是一样的 注册中心一般都是集群的形式存在保证高可用 consul像是一个nosql 存储着键值对 可以做存储consul是c/s架构 ...
- .net code+vue 文件上传
后端技术 .net code 官方文档 https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/file-uploads?view=aspnet ...
- 让源码包apache服务被服务管理命令识别
在默认情况下,源码包服务是不能被系统的服务管理命令所识别和管理的,但是如果我们做一些设定,则也是可以让源码包服务被系统的服务管理命令所识别和管理的.不过笔者并不推荐大家这样做, 因为这会让本来区别很明 ...
- Maven 知识点总结以及解决jar报冲突的几种方法
1.常见的命令 Compile Test Package Install Deploy Clean 2.坐标的书写规范 groupId 公司或组织域名的倒序 artifactId 项目名或模块名 ve ...
- ChannelNets: 省力又讨好的channel-wise卷积,在channel维度进行卷积滑动 | NeurIPS 2018
Channel-wise卷积在channel维度上进行滑动,巧妙地解决卷积操作中输入输出的复杂全连接特性,但又不会像分组卷积那样死板,是个很不错的想法 来源:晓飞的算法工程笔记 公众号 论文: C ...
- muduo 网络库的整体架构图和一个简化版本的架构设计
https://blog.csdn.net/adkada1/article/details/54342275 简析 https://blog.csdn.net/amoscykl/article/det ...
- Java Web工作原理
解析HTTP协议 HTTP:超文本传输协议(HyperText Transfer Protocol) HTTP是一种无状态的协议,意思是指在Web浏览器和Web服务器之间不需要建立持久的连接. HTT ...