今日知识
函数复习,生成器函数复习,协程入门,协程VS多线程,协程版生产者消费者模型
函数的复习
函数的定义与调用
在不执行代码的情况下
需要明确代码是如何一步一步执行的
def func():
print(1)
return 2
print(2)
res = func()
print(res)
生成器函数复习
把return换为yield
函数就会变为一个生成器
只要函数中有了yield,那么这个函数就是生成器函数
对象的创建 = 类名()
生成器的创建 = 生成器函数名()
希望让生成器对象执行起来
生成器对象.__next__
()
生成器对象运行起来后
它会执行函数内部的代码
直到遇见yield就会停住
返回yield后面的数据
并且指针状态会停留在yield这一行的行未
当第二次执行
生成器对象.__next__
()
从上一次停留的地方继续往下走
情况1,直到遇见yield就会停住,返回yield后面的数据
情况2,没有遇到yield,函数结束,它会按照迭代器协议报错。得不到数据,经协过的代码仍会被执行.
测试代码
def func():
print(1)
yield 2
print(22)
res = func()
print(res)
n = res.__next__()
print(n)
n = res.__next__()
print(n)
# for i in res:
# print(i)
另一种方式让生成器函数跑起来
方式一
生成器对象.__next__
()
方式二
next(生成器对象)
def func():
print(1)
yield 2
print(22)
res = func()
n = next(res)
print(n)
n = next(res)
print(n)
generator
英 [ˈdʒenəreɪtə®] 美 [ˈdʒenəreɪtər]
n.
发电机;发生器;电力公司
iter函数
下面两种情况是一样的效果
iter_obj = res.__iter__()
iter2_obj = iter(res)
效果有点像
li = [1,2,3]
li.sort()
sorted(li)
协程
生成器的进阶
复习函数生成器的特点
关注函数内的代码什么时候会被执行
使用yield协程计算大量的数据
由于两个生成器对象是切换占用cpu,执行任务
不会发生同一时刻抢的情况
因此不用加锁,不存在数据的混乱
当前执行,使用了一个进程,一个主线程来完成的
import time
isum = 0
def addnum():
global isum
for i in range(10000000):
isum += 1
yield isum
g1 = addnum()
g2 = addnum()
start_time = time.time()
for i in range(10000000):
next(g1)
next(g2)
print(isum)
end_time = time.time()
cost_time = end_time - start_time
print('花费了%.f秒' % cost_time)
对比通过多线程来计算,不加锁的情况
结果,多线程完败协程
多线程执行同样代码,耗时11秒,协程砂时4秒
import time
isum = 0
def addnum():
global isum
for i in range(10000000):
isum += 1
yield isum
g1 = addnum()
g2 = addnum()
start_time = time.time()
for i in range(10000000):
next(g1)
next(g2)
print(isum)
end_time = time.time()
cost_time = end_time - start_time
print('花费了%.f秒' % cost_time)
进程线程协程
进程,分配资源的单位
进程中必定有一个主线程
线程,是执行任务的单位
协程,它在线程之内。我们利用了生成器它可以保存状态,可以暂停可以恢复。人为行设计出来的一种多任务的模式。
进程包含线程,线程包含协程。
协程的应用
生产者消费者模型,两个版本
关注切换的主动权在谁
你拍一我拍一双人共同玩游戏
生成器send方法
greenlet提高切换效率
yield会返回
用greenlet对象直接通过switch方法切换到另一函数中
greenlet的用法
导入模式
import greenlet
greenlet
一种绿色小鸟
创建对象
对象 = greenlet.greenlet(test1)
运行
对象.switch()
switch
英 [swɪtʃ] 美 [swɪtʃ]
n.
(电路的)开关,闸,转换器;(尤指突然彻底的)改变,转变;(铁路的)转辙器,道岔
v.
(使)改变,转变,突变;交换;掉换;转换;对调;调班;临时掉换工作时间
明确,为什么要多任务
原因
不想等
真正开发的过程中
不能够明确什么时候要等,什么时候要等多久
a 烧水 15 1 1
b 煮汤 30 1 1
c 挂机 1h 1 1
gevent
event
英 [ɪˈvent] 美 [ɪˈvent]
n.
发生的事情;(尤指)重要事情,大事;公开活动;社交场合;(体育运动的)比赛项目
导入模块
import gevent
创建对象
对象 = gevent.spawn(函数名)
spawn
英 [spɔːn] 美 [spɔːn]
v.
产卵;引发;引起;导致;造成
n.
(鱼、蛙等的)卵
运行
对象.join()
当gevent对象遇到join方法的阻塞时,就会自动去gevent对象中去看
有没有可以执行的代码
查询范围是所有的对象,不仅仅是只查自己对应的函数的代码
导入猴子
from gevent import monkey
monkey.patch_all()
gevent对象不能够识别常见的一些耗时操作
因此需要猴子来打一个补丁
注意,只能使用from…import来导入
不可以通过gevent.monkey.patch_all()调这个函数
monkey
英 [ˈmʌŋki] 美 [ˈmʌŋki]
n.
猴子;顽皮的孩子;调皮鬼;捣蛋鬼;500英镑
第三人称单数: monkeys 复数: monkeys 现在分词: monkeying 过去式: monkeyed 过去分词: monkeyed
patch
英 [pætʃ] 美 [pætʃ]
n.
色斑;斑点;(与周围不同的)小块,小片;补丁;补块;眼罩
v.
打补丁;缝补;修补
第三人称单数: patches 复数: patches 现在分词: patching 过去式: patched 过去分词: patched
CET4考研TOEFLT
all
英 [ɔːl] 美 [ɔːl]
det.
所有;全部;全体;一切;(与单数名词连用,表示某事在某段时间内持续发生)全部的,整个的
pron.
所有;全部;全体;一切;唯一的事物;所有的事物
adv.
完全;很;十分;非常;太;过分
select
select模块的使用:
select会监听socket或者文件描述符的I/O状态变化,并返回变化的socket或者文件描述符对象
select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
这是Python select方法的原型,接收4个参数
- rlist:list类型,监听其中的socket或者文件描述符是否变为可读状态,返回那些可读的socket或者文件描述符组成的list
- wlist:list类型,监听其中的socket或者文件描述符是否变为可写状态,返回那些可写的socket或者文件描述符组成的list
- xlist:list类型,监听其中的socket或者文件描述符是否出错,返回那些出错的socket或者文件描述符组成的list
- timeout:设置select的超时时间,设置为None代表永远不会超时,即阻塞。
注意:
Python的select方法在Windows和Linux环境下的表现是不一样的,Windows下它只支持socket对象,不支持文件描述符(file descriptions),而Linux两者都支持。
我们可以通过打印来查看select模块提供的作用,返回的rlist,wlist只会返回有改变的监听对象,如果没有改变的函数,那么整个程序会阻塞住
如果我们想要加入新的连接,那么我们只需要把连接对象放进rlist即可,当有数据过来的时候,那么连接就会发生改变(文件描述符),select函数就会帮我们监听到
如果我们想发送数据,那么我们可以把conn加入到wlist,因为发送数据需要我们去输出流数据,然后等待select把wlist里面的消息取出来,我们就可以发送数据了