1.什么是生成器?
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个可以迭代的东西。
2.next()与send()
next()方法:
在调用生成器运行的过程中,每次遇到 yield ,函数返回当前的值,并且会暂停并保存当前所有的运行信息, 并在下一次执行 next() 方法时从当前位置继续运行。
实例:
def _generator():
r = "Here"
for i in range(3):
yield r
r = '200 OK' + str(i)
g = _generator() # 生成器对象
first_next = g.__next__() # 第一次next
second_next = g.__next__() # 第二次next
third_next = g.__next__() # 第三次next
print(first_next)
print(second_next)
print(third_next)
运行结果:
Here
200 OK0
200 OK1
代码解读:
首先,定义的 g=_generator() 并不是函数调用,而是产生生成器对象。
第一次,我们调用next()方法,即:first_next = g.__next__(),
此时我们进入生成器函数_generator(),遇到yield返回,此时,r="Here",
所以,可以看到结果第一行为:"Here"。
第二次,我们同样调用了next()方法,即:second_next = g.__next__(),
此时我们也是进入生成器,但是并不是从头开始进入执行,而是从上一次的yield后面开始,
即:执行 r = '200 OK' + str(i),此时 r = '200 OK0',遇到yield返回,
所以,可以看到结果第一行为:"200 OK0"。
第三次,我们依旧调用了next()方法,即:third_next = g.__next__(),
此时我们也是进入生成器,但是并不是从头开始进入执行,而是从上一次的yield后面开始,
即:执行 r = '200 OK' + str(i),此时 r = '200 OK1',遇到yield返回,
所以,可以看到结果第一行为:"200 OK1"。
如果我们再加多有一次调用,forth_next = g.__next__() # 第四次next,
就会报错:
Traceback (most recent call last):
File "C:/Users/10475/Desktop/Spider_Project/Scrapy_Project/tools/multiprocessing_demo.py", line 76, in <module>
forth_next = g.__next__() # 第四次next
StopIteration
原因是:
执行3次yield后,此时,i已经为2了,顶天了,已经没有yield可以执行了,
所以,第4次调用next(o)就报错。
在平时使用生成器,我们更多的去使用for循环去迭代生成器,上面也提到,
generator是一个可以迭代的对象,所以结果也是一样的。
实例改写:
def _generator():
r = "Here"
for i in range(3):
yield r
r = '200 OK' + str(i)
g = _generator() # 生成器对象
for i in g:
print(i)
send()方法:
先理解个概念【挂起】:意思就是暂时保留先不进行,等待需要时再进行。
作用:与next()作用相似
区别:
1.send(value)可以传递value给yield,即:我们可以指定yield返回啥就返回啥,
2.next()不能传递特定的值,只能传递None进去。
第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,可以看到,can't send non-None value to a just-started generator
因为生成器just-started generator
,是没有Python yield语句来接收这个值的。
Traceback (most recent call last):
File "C:/Users/10475/Desktop/Spider_Project/Scrapy_Project/tools/multiprocessing_demo.py", line 87, in <module>
g.send(1)
TypeError: can't send non-None value to a just-started generator
实例1:
def _generator():
for i in range(4):
n = yield i
if n == 'hello':
print('world')
else:
print(str(n))
g = _generator() # 生成器对象
print(g.__next__()) # 结果是:0
print(g.__next__()) # 结果是:None
运行结果:
0
None
1
代码解读:
第一次调用g.__next__(),跑到 yield i,遇到yield,返回结果为0。
第二次调用g.__next__(),继续从上一次状态继续执行,此时,需要注意,
我们执行的起点是:n = yield i,并且这个n值并不是i值,而是通过send()传递过来的值,
即:n = send(),但是我们没有调用send()方法,所以,n自然而然为None,
所以,此时是执行print(g.__next__()),结果为None。
紧接着,我们还在继续,yield完以后,n就有值了,此时为None,
我们进入判断语句,此时是执行print(str(n)),结果为1。
实例2:
def _generator():
for i in range(4):
n = yield i
if n == 'hello':
print('world')
else:
print(str(n))
g = _generator() # 生成器对象
# print(g.__next__())
print(g.send(None)) # 相当于g.__next()__
代码解读:
调用send()方法,传入value为None,此时相当于next()方法,效果自然与next()一样,
即:遇到yield就返回,返回结果为:0,所以print(0)。
实例3:
def _generator():
for i in range(4):
n = yield i
if n == 'hello':
print('world')
else:
print(str(n))
g = _generator() # 生成器对象
g.send(None) # 相当于g.__next__()
g.send('100')
g.send('Python')
g.send('hello')
运行结果:
100
Python
world
代码解读:
g.send(None):
相当于g.__next__()作用,遇到yield返回,返回一个0,由于没有谁打印0,自然看不到。
g.send('100'):
继续从上一次状态出发,起点为:n ,(再次提醒:n 不等于 yield i)此时n = send(100),
即:n = 100,进入判断,print(str(n)),结果也就是我们看到的第一行100。
g.send('Python'):
继续上一次状态出发,起点为:for i in range(4),
此时n = send('Python'),即:n = 'Python',进入判断,print(str(n)),
结果也就是我们看到的第二行"Python"。
g.send('hello'):
继续上一次状态出发,起点为:for i in range(4),
此时 n = send('hello'),即:n = 'hello',进入判断,print('world'),
结果也就是我们看到的第三行"world"。
如果我们再加多一行:g.send('Scrapy'),结果为:
Traceback (most recent call last):
100
File "C:/Users/10475/Desktop/Spider_Project/Scrapy_Project/tools/multiprocessing_demo.py", line 91, in <module>
Python
world
Scrapy
g.send('Scrapy')
StopIteration
为什么会报错?
因为超出了range(4),前面已经实现了4次,(send(None)也要算上),
这一次加了g.send('Scrapy')是第五次,所以报错。
为什么还会有输出?
我的理解是:虽然报错,但是,我压根都不需要你这个i,我的n = send('Scrapy'),
即:n = "Scrapy",依旧进入循环,依旧print(str(n)),所以有输出。
结束语:
最近在看爬虫的协程的相关资料,提到了生成器,所以写下这篇文章,仅此记录一下,借鉴了一下文章的内容:
https://blog.csdn.net/yueguanghaidao/article/details/10201327
https://blog.csdn.net/weixin_30363263/article/details/79675898
加上自己的理解,自己动手,才能学到更多~加油!