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的更多相关文章

  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. bash5.0参考手册

    Bash Reference Manual a.summary-letter { text-decoration: none } blockquote.indentedblock { margin-r ...

  2. 力软最新版本与.netCore版本

    功能强大,直接上图: 加微信或QQ交流开发技术:25489181 netcore版本 版本优势: .NET Core是适用于 Windows.Linux 和 macOS 的免费.开源托管的计算机软件框 ...

  3. SAP里会话结束方法(杀死进程)

    在SAP的ERP里,有很多方法可以结束一个会话,然而在不同情况下,需要使用的方法也不同.下面从先后顺序来简单说明:1.SM04:最常用的方法,在SM04点击工具栏的会话->结束会话,来关闭一个会 ...

  4. 容器编排系统K8s之包管理器helm基础使用(二)

    前文我们介绍了helm的相关术语和使用helm安装和卸载应用,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14305902.html:今天我们来介绍下自定义 ...

  5. 前端面试准备笔记之JavaScript(02)

    01. this的典型应用场景 this在各个场景中取什么值,是在函数执行的时候确认的,不是在定义的时候确认的. 普通函数执行 返回window function fn1() { console.lo ...

  6. synchronized的jvm源码分析聊锁的意义

    上篇写完了ReentrantLock源码实现,从我们的角度分析设计锁,在对比大神的实现,顺道拍了一波道哥的马屁,虽然他看不到,哈哈.这一篇我们来聊一聊synchronized的源码实现,并对比reen ...

  7. HA工作机制及namenode向QJM写数据流程

    HA工作机制 (配置HA高可用传送门:https://www.cnblogs.com/zhqin/p/11904317.html) HA:高可用(7*24小时不中断服务) 主要的HA是针对集群的mas ...

  8. Linux下nf_conntrack(最全面)_董明磊-CSDN博客_nf_conntrack https://blog.csdn.net/qq_35299863/article/details/79530732

    Linux下nf_conntrack(最全面)_董明磊-CSDN博客_nf_conntrack https://blog.csdn.net/qq_35299863/article/details/79 ...

  9. 并发编程:Actors 模型和 CSP 模型

    https://mp.weixin.qq.com/s/emB99CtEVXS4p6tRjJ2xww 并发编程:Actors 模型和 CSP 模型 ImportNew 2017-04-27    

  10. Java中get()和set()作用

    一.前言 在程序的设计中,我们普遍的定义一个private变量,然后为这个变量加上get() .set()方法,那为何不直接定义一个public变量呢? 很多人是这样回答的: 1.关系到安全性的问题, ...