早期的没有标准的协程库的时候,tornado内部就是通过自定义的yield的方式来实现协程的。
那说明我们的协程和yield的生成器是有一定的关系,什么是生成器呐?
如果你可以,可以回顾一下之前我梳理的一些知识点。
python面向对象编程篇(16):你应该需要掌握了解的-迭代器和生成器这里也无妨重复再多说一下:
python种的生成器和迭代器是息息相关滴,而迭代器又和可迭代对象等有关系。
这里补充一下概念理解:
- 通过iter()方法获得了list的迭代器对象
- 通过和next()访问list中元素对象
- 如果容内已无没有可访问的元素,继续调用next()方法将会抛出一个StopIteration异常终止迭代器
- for语句已封装了关于__iter__()和next()自动调用
❝PS:通常我们对一个iterable用for ... in ...进行迭代的时候实际是先通过调用iter()方法得到一个iterator,可迭代的对象A,,然后循环地调用可迭代对象A的next()方法取得每一次的值,直到iterator为空,返回的StopIteration作为迭代器循环结束的标志。
- 1)列表、元组、集合、字典、字符串等等之类的对象被称为可迭代对象
- 2)任何你可以循环的对象都是可迭代对象
- yield是对生成器更优雅一种封装,所以它是一个特殊的迭代器。
- yield 是生成器实现__next__()方法的关键。它作为生成器执行的暂停恢复点,可以对yield表达式进行赋值,也可以将yield表达式的值返回。
- __next__()方法被调用时,既可以计算返回值,还可以记住当前函数已执行的状态,下次__next__()的调用,会从上次停止的地方开始。
- yield可以暂停一个函数并返回此时的中间结果。该函数将保存执行环境并在下一次恢复。
(这里我们就可以大致的理解到,关于协程是如何的异步,当调用next的时候可以记录当前的状态,等到下次next()的时候IO完成的时候再恢复)
生成器有两种表示:
def ceshi():
yield "你好我是你大爷!"
yield "你好我是你二大爷!"
yield "你好我是你三大爷!"
# 生产一个对象
opj =ceshi()
# 吧对象丢到next
print('第1次调用:',next(opj))
print('第2次调用:',next(opj))
print('第3次调用:',next(opj))
print('第4次调用:',next(opj))
输出结果
StopIteration
第1次调用: 你好我是你大爷!
第2次调用: 你好我是你二大爷!
第3次调用: 你好我是你三大爷!
Traceback (most recent call last):
print('第4次调用:',next(opj))
StopIteration
上面的示例也可以直接的而是要FOR进行生成器的调用:
def ceshi():
yield "你好我是你大爷!"
yield "你好我是你二大爷!"
yield "你好我是你三大爷!"
# # 生产一个对象
# opj =ceshi()
# # 吧对象丢到next
# print('第1次调用:',next(opj))
# print('第2次调用:',next(opj))
# print('第3次调用:',next(opj))
# print('第4次调用:',next(opj))
for i in ceshi():
print('FOR:方式',i)
异步网络执行的简单示例:
方式1:
# 是一个生成器的对象
import requests
def ceshi():
yield requests.get("http://www.baidu.com").status_code
yield requests.get("http://www.taobao.com").status_code
yield requests.get("http://www.qq.com").status_code
# 生产一个对象
opj =ceshi()
# 吧对象丢到next
print('第1次调用:',next(opj))
print('第2次调用:',next(opj))
print('第3次调用:',next(opj))
方式2:
# 是一个生成器的对象
import requests
def ceshi():
yield requests.get("http://www.baidu.com").text[10:50]
yield requests.get("http://www.taobao.com").text[10:50]
yield requests.get("http://www.qq.com").text[10:50]
# 生产一个对象
opj =ceshi()
def run(opj):
# 启动了生成器 调用了
n=0
while n<=2:
n=n+1
#send一些 value 发回到暂停执行的函数
resutl =opj.send(None)
print(resutl)
opj.close()
run(opj)
输出结果:
html>
<!--STATUS OK--><html> <head><met
html>
<html lang="zh-CN">
<head>
<tit
上面网络请求:发起网络的请求其实会遇到IO阻塞,按以前我们的同步模式的话,此时一般需要等待这个IO阻塞完成才会下一步,但是现在生成器不一样了,我丢出去请求后,我就不管了,直接的返回了!所以一个线程下我就可以同时跑三个网络的请求了!!!!!且是很快的丢出去!不等待!
❝PS:
第一次调用send()激活生成器时,value值必须为None,因为当前还没有yield表达式可以接收参数。 yield表达式有一个返回值,send方法的作用就是控制这个返回值,send的参数就是yield表达式的返回值 激活生成器的方法有:next和send,其中send的主要给我们的已暂停执行的函数中,传入值。
既然我们的生成器可以暂停和可以继续恢复,那肯定是有所谓的状态机附体。那我们的生成器有哪几种状态呐?生成器的四种状态,分别是:
那如何知道我们的生成器的当前的状态呐?
通过inspect库中的getgeneratorstate的方法可以查看:
import requests
from inspect import getgeneratorstate #一定要导入
def ceshi():
yield requests.get("http://www.baidu.com").status_code
yield requests.get("http://www.taobao.com").status_code
yield requests.get("http://www.qq.com").status_code
# 生产一个对象
opj =ceshi()
def run(opj):
# 启动了生成器 调用了
n=0
while n<=2:
n=n+1
print("生成器状态为--激活前:{0}".format(getgeneratorstate(opj)))
# 开始激活生成器
resutl =opj.send(None)
print("生成器状态为--激活后:{0}".format(getgeneratorstate(opj)))
print(resutl)
print("生成器状态为--关闭前:{0}".format(getgeneratorstate(opj)))
opj.close()
print("生成器状态为--关闭后:{0}".format(getgeneratorstate(opj)))
run(opj)
输出结果:
生成器状态为--激活前:GEN_CREATED
生成器状态为--激活后:GEN_SUSPENDED
200
生成器状态为--激活前:GEN_SUSPENDED
生成器状态为--激活后:GEN_SUSPENDED
200
生成器状态为--激活前:GEN_SUSPENDED
生成器状态为--激活后:GEN_SUSPENDED
200
生成器状态为--关闭前:GEN_SUSPENDED
生成器状态为--关闭后:GEN_CLOSED
总结:从上面简单示例我们可以看到单进程单线程的并发的异步处理,有了协程的影子!生成器和协程大致雏形上类似。
- yield :是返回一个元素
- yield from xxxxxx:返回是一个新的生成器对象
- yield from 后面可以返回:生成器 、元组、 列表、等可迭代对象
- yield from iterable本质上等于 for item in iterable: yield item
加入我们的生成器中存在对应条件且直接终止我们的生成器的迭代过程的话:会怎么样?
1):使用for循环我们的迭代的情况
❝PS: for可以自动帮处理StopIteration异常
def ceshi():
yield "你好我是你大爷!"
yield "你好我是你二大爷!"
return "你好我是你三大爷!"
for i in ceshi():
print('FOR:方式',i)
输出的结果:
FOR:方式 你好我是你大爷!
FOR:方式 你好我是你二大爷!
2):使用next循环我们的迭代的情况
def ceshi():
yield "你好我是你大爷!"
yield "你好我是你二大爷!"
return "你好我是你三大爷!"
opj= ceshi()
# 吧对象丢到next
print('第1次调用:',next(opj))
print('第2次调用:',next(opj))
print('第3次调用:',next(opj))
输出结果:
第1次调用: 你好我是你大爷!
第2次调用: 你好我是你二大爷!
File "D:/code/python/local_python/xxxxx.py", line 28, in <module>
Traceback (most recent call last):
print('第3次调用:',next(opj))
StopIteration: 你好我是你三大爷!
3)加上异常捕获的情况:
def ceshi():
yield "你好我是你大爷!"
yield "你好我是你二大爷!"
return "你好我是你三大爷!"
opj= ceshi()
# 吧对象丢到next
try:
print('第1次调用:',next(opj))
print('第2次调用:',next(opj))
print('第3次调用:',next(opj))
except StopIteration as e:
print(e.value)
输出结果:
第1次调用: 你好我是你大爷!
第2次调用: 你好我是你二大爷!
你好我是你三大爷!
从上面的情况可以看出:
如何和改造一下生成器的对象可以使用for迭代的时候,也可以获取我们最后return的值呐?
之前其实也有详细讲述过,可以参考:
python面向对象编程篇(17):生成器之 yield 和yield from 表达式的实践
def ceshi():
yield "你好我是你大爷!"
yield "你好我是你二大爷!"
return "你好我是你三大爷!"
# 本质上这个也还是一个生成器,因为有yield
def ceshi_yield_form(generator):
result =yield from generator
# 获取到了我们的return的值
print('我们的return的值:',result)
# 吧对象丢到next
opj= ceshi()
for i in ceshi_yield_form(opj):
print(i)
输出:
你好我是你大爷!
你好我是你二大爷!
我们的return的值: 你好我是你三大爷!
上面的例子:
以上仅为个人的总结认知,如有如有不当之处,还望各位大佬指正!
❝个人其他博客地址
❝简书:https://www.jianshu.com/u/d6960089b087
掘金:https://juejin.cn/user/2963939079225608
小钟同学 | 文 【原创】| QQ:308711822
1:本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。 2:关于文章内容,有部分内容参考自互联网整理,如有链接会声明标注;如没有及时标注备注的链接的,如有侵权请联系,我会立即删除处理哟。
原文作者:程序员小钟同学;如有侵权,请联系管理员删除。