4 Python高级 协程

1 迭代器

可迭代对象

遍历也叫迭代。

from collections import Iterable,Iterator

class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self,name):
        self.names.append(name)
    def __iter__(self):
    #如果想要一个对象称为可迭代对象 即可以使用for那必须实现__iter__方法
        return Mates(self)#__iter__方法的返回值是一个迭代器(要有iter和next方法)
        #Classmate把自己的引用(self)给Mates,就是地址
class Mates(object):
    def __init__(self,obj):
        self.obj = obj
        self.current_num = 0
    def __iter__(self):
        pass
    def __next__(self):
        if self.current_num < len(self.obj.names):
            ret = self.obj.names[self.current_num]
            self.current_num +=1
            return ret
        else:
            raise StopIteration#抛出这个异常 for循环就自动停了
classmate = Classmate()
classmate.add('123')
classmate.add('234')
classmate.add('345')
#print('calssmate 是否是可迭代对象 ',isinstance(classmate,Iterable))
# classmate_iterator = iter(classmate)
# #iter函数会自动调用classmate的iter方法  iter方法返回的是Mates对象的引用
# print('classmate_iterator 是否是可迭代器 ',isinstance(classmate_iterator,Iterator))
# print(next(classmate_iterator))
for i in classmate:
    print(i)

iter方法返回自己

class Classmate(object):
    def __init__(self):
        self.names = list()
        self.current_num = 0
    def add(self,name):
        self.names.append(name)
    def __iter__(self):
    #如果想要一个对象称为可迭代对象 即可以使用for那必须实现__iter__方法
        return self#__iter__方法的返回值是一个迭代器(要有iter和next方法)
    def __next__(self):
        if self.current_num < len(self.names):
            ret = self.names[self.current_num]
            self.current_num +=1
            return ret
        else:
            raise StopIteration#抛出这个异常 for循环就自动停了
classmate = Classmate()
classmate.add('123')
classmate.add('234')
classmate.add('345')
for i in classmate:
    print(i)

range函数产生一个列表用于后续处理,但是xrange返回的并不是一个列表,而是一个迭代器,这样做的好处就是节省了空间。xrange配合for循环的遍历效果等同于rnage,数据量很大使用xrange。

斐波那契数列 迭代器

class fin(object):
    def __init__(self,all_num):
        self.all_num = all_num
        self.count = 0
        self.a = 0
        self.b = 1
    def __iter__(self):
        return self
    def __next__(self):
        if self.count < self.all_num:
            ret = self.a
            self.a,self.b = self.b,self.a+self.b
            self.count+=1
            return ret
        else:
            raise StopIteration
fi = fin(10)
for num in fi:
    print(num)

2 生成器(特殊的迭代器)

方法一

nums = [x*2 for x in range(10)]#列表推导式  占用空间
print(nums)
num = (x*2 for x in range(10))#生成器。返回的是生成这个数据的方式
for i in num:
    print(i)

方法二

generator很强大,如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数实现。

def create_num(all_num):
    a, b = 0, 1
    cruuent_num = 0
    while cruuent_num < all_num:
        #print(a)
        yield a#如果一个函数中有yield 那么这个就不是函数了而是一个生成器的模版
        a, b = b, a+b
        cruuent_num += 1
#如果在调用create_num的时候 发现这个函数中有yield 那么此时 不是调用函数 而是创建一个生成器对象
obj = create_num(10)
#next执行生成器
ret = next(obj)
# for num in obj:
#     print(num)
可以创建多个生成器对象
obj2 = create_num(10)

通过异常判断生成器已经结束

def create_num(all_num):
    a, b = 0, 1
    cruuent_num = 0
    while cruuent_num < all_num:
        #print(a)
        yield a#如果一个函数中有yield 那么这个就不是函数了而是一个生成器的模版
        a, b = b, a+b
        cruuent_num += 1
    return 'ok'
#如果在调用create_num的时候 发现这个函数中有yield 那么此时 不是调用函数 而是创建一个生成器对象
obj = create_num(50)
while True:
    try:
        ret = next(obj)
        print(ret)
    except Exception as ret:
        print(ret.vaule)#想要获得生成器中的返回值 用捕获异常后的ret的vaule值
        break

send唤醒生成器

def create_num(all_num):
    a, b = 0, 1
    cruuent_num = 0
    while cruuent_num < all_num:
        ret = yield a#ret的值来源于send传递的参数
        print('...ret ..',ret)
        a, b = b, a+b
        cruuent_num += 1
obj = create_num(50)
#obj.send(None) send一般不会放到第一次启动生成器 如果非要这样 那么就传递None
ret = next(obj)
print(ret)
#send里面的数据会当作yield a的结果 然后ret保存这个值
#send的结果是下一次调用yield时 yield后面的值
ret = obj.send('呵呵')#send可以传参数
print(ret)

迭代器减少内存空间 实现循环  生成器可以让函数暂停执行 在通过next send再次执行

3 使用yield实现多任务(协程). 并发

def task_1():
    while True:
        print('....1....')
        yield
def task_2():
    while True:
        print('....2....')
        yield
def main():
    t1 = task_1()
    t2 = task_2()
    while True:
        next(t1)
        next(t2)#t1 t2交替运行 实现多任务
if __name__ == '__main__':
    main()

greenlet gevent完成多任务

from greenlet import greenlet
def task_1():
    while True:
        print('....1....')
        gr2.switch()
def task_2():
    while True:
        print('....2....')
        gr1.switch()

gr1 = greenlet(task_1)#greenlet里面已经封装
gr2 = greenlet(task_2)
#切换到gr1中运行
gr1.switch() #相当于switch替换了next

gevent模块可以自动切换任务

原理就是当一个greenlet遇到IO(指的是input output 输入 输出 比如网络 文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet。等到IO操作完成 再在适当的时候切换回来继续执行。

由于IO操作非常耗时 经常使程序处于等待状态 有了gevent为我们自动切换协程 就保证总有greenlet在运行 而不是等待IO。

import gevent

def f(n):
    for i in range(n):
        print(gevent.getcurrent(),i)
        #用来模拟耗时操作 注意不是time模块中的sleep
        gevent.sleep(0.5)
g1 = gevent.spawn(f,5)
g2 = gevent.spawn(f,5)
g3 = gevent.spawn(f,5)
g1.join()
g1.join()
g1.join()

给程序打补丁

如果想要用time模块中的sleep  需要gevent的monkey模块. 他会自己替换。

import gevent
from gevent import monkey
import time
monkey.patch_all()
def f(n):
    for i in range(n):
        print(gevent.getcurrent(),i)
        time.sleep(0.5)
gevent.joinall([gevent.spawn(f,5),gevent.spawn(f,5),gevent.spawn(f,5)])
#不用都单独join

进程 线程 协程对比

  • 进程是资源分配的单位。
  • 线程是操作系统调度的单位。
  • 进程切换需要的资源很大,效率很低。
  • 协程切换任务资源很小,效率高。
  • 多进程 多线程根据cpu核数不一样可能是并行的 但是协程是在一个线程中 所以是并发。

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值