Python 生成器的next和send

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

原因是:
执行3yield后,此时,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
加上自己的理解,自己动手,才能学到更多~加油!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值