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. Vue MVVM模型原理

    最近反思了下自己,觉得自己很急躁,学技术总是觉得能用就行了,其实这样很不好,总是这样,就永远只能当用轮子的人.好了,废话不多说,转入正题: 要理解MVVM的原理,首先要理解它是什么,怎么运作起来的: ...

  2. commons-lang3相关类实例

    一.ArrayUtils //1.判断两个数组长度是否相等 ArrayUtils.isSameLength(new int[] {1,2,3,4}, new int[] {1,2,3,4});//tr ...

  3. 【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务

    欢迎使用 Blazor!Blazor 是一个使用 .NET 生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建信息丰富的交互式 UI. 共享使用 .NET 编写的 ...

  4. 【Android】编译报错 Annotation processors must be explicitly declared now 解决方案

    问题 在网上下载一个demo,因为版本久远,里面添加了本地 Butter Knife 的jar包,在编译时报错 Annotation processors must be explicitly dec ...

  5. k8s之共享存储概述以及演示

    共享存储机制 k8s对有状态的容器应用或者需要对数据进行持久化的应用,在之前的篇章说过,可以将容器内的目录挂载到宿主机的容器目录或者emptyDir临时存储卷. 另外,k8s还开放了两个资源,分别是P ...

  6. 图解ARP协议

    前置知识:MAC地址 在说到ARP协议之前,需要了解MAC地址,在OSI七层模型中,第三层是网络层,要解决的是下一跳机制确定的设备位置,具体来说就是网卡地址,MAC地址用于在网络中唯一标示一个网卡,一 ...

  7. 如何应对C语言内存泄露! 华为开发者社区 2020-09-29

    如何应对C语言内存泄露! 华为开发者社区 2020-09-29

  8. Ubuntu16 安装 OpenSSH-Server

    Ubuntu16.04 桌面版默认是没有安装 SSH 服务的,需要手动安装服务: 更新源:sudo apt-get update 安装服务:sudo apt-get install -y openss ...

  9. 【PY从0到1】第七节 函数

    # 7 第七节 函数 # 函数对于编程语言来说是一块重量级的内容. # 他可以实现或者简化编写的代码. # 编写好特定功能的函数后,就可以重复调用函数来完成任务. # 下面我们就用函数的形式来封装前面 ...

  10. linux常用命令(shell脚本常用命令)(grep、cut、sort、uniq、seq、tr、basename、dirname)

    本章命令: 1 2 3 4 5 6 grep cut sort uniq seq tr 1.grep 作用:过滤文本内容 选项 描述 -E :--extended--regexp 模式是扩展正则表达式 ...