python协程回顾

1. 迭代器

1.1 迭代

通过迭代器不断取出可迭代对象中的下一个元素的值 的过程

for

1.2 可迭代对象

能能被迭代的对象

from collections import Utterable

isinstance(对象, Iterable)

实现    __iter__提供迭代器

 1from collections import Iterable
 2
 3data = [1,2,3,4,5]
 4for i in data:
 5    print(i)
 6
 7# 1 通过一个记录位置的对象  不断地间接访问 对象中数据的过程  - 迭代
 8# 2 可迭代对象 - 可以被迭代的对象  for循环
 9# 3 如何判断一个对象是否是可迭代类型的对象  isinstance(对象, 类型)
10#   常见的容器类型 就是典型的可迭代类型
11print("判断data是否是列表类型", isinstance(data, list))
12# 4 自定义可迭代类型 实现类中 __iter__方法 的类就是可迭代类型
13class MyRange(object):
14    def __init__(self, n):
15        self.n = n
16    def __iter__(self):
17        """实现一个可迭代类型  一定要实现本方法 返回一个迭代器"""
18        pass
19
20print("判断MyRange是否是可迭代类型", isinstance(MyRange(5), Iterable))
21
22
23for i in MyRange(5):
24    print(i)
25
26
27
28print("判断data是否是可迭代类型", isinstance(data, Iterable))
29print("判断整数是否是可迭代类型", isinstance(123, Iterable))
30print("判断元组是否是可迭代类型", isinstance((1,2,3), Iterable))
31print("判断字符串是否是可迭代类型", isinstance("123456", Iterable))
32print("判断字符串是否是可迭代类型", isinstance({1:2}, Iterable))

1.3 迭代器

迭代的访问 可迭代对象中的下一个元素

应用场景:  用户只需要关心如何使用迭代器访问数据 而不需要关键数据该如何访问

解耦合

每一种可迭代对象都会提供对应的迭代器

from collections import Iterator

isinstance(对象,Iterator)

实现

__next__提供下一个元素的值

__iter__返回自身即可  应对情况 迭代器 = iter(迭代器)

 1from collections import Iterator,Iterable
 2
 3# 1 迭代器Iterator- 记录每次访问的位置信息的对象  便于下一次访问 (当访问完成后会自动执行下一个位置)
 4# 2 应用场景 不同可迭代对象有不同的访问方式  每一种可迭代对象都会提供一个迭代器
 5#           用户只需要通过迭代器取出 下一个元素 的值即可 而不用具体访问细节 - 解耦合
 6
 7# 3 自定义迭代器类型 实现__next__方法
 8class MyRangeIter(object):
 9    """迭代器类"""
10    def __init__(self, n):
11        self.n = n
12        self.i = 0
13    def __next__(self):
14        """实现迭代器类型一定要实现该方法  目的:返回用户需要的下一个元素的值"""
15        if self.i < self.n:
16            self.i += 1
17            return self.i
18        else:
19            # 停止 迭代    当迭代器迭代完成的时候 抛出这个异常
20            raise StopIteration
21    def __iter__(self):
22        """Python规定迭代器 必须是可迭代对象 因此也必须使用该方法"""
23        return self
24class MyRange(object):
25    """可迭代类型"""
26    def __init__(self, n):
27        self.n = n
28    def __iter__(self):
29        """实现一个可迭代类型  一定要实现本方法 目的: 返回一个迭代器"""
30        return MyRangeIter(self.n)
31# 4 判断迭代器类型
32print("判断迭代器类型",isinstance(MyRangeIter(5), Iterator))
33print("判断迭代器是否是可迭代对象",isinstance(MyRangeIter(5), Iterable))
34print("判断可迭代对象是否是迭代器",isinstance(MyRange(5), Iterator))
35for i in MyRange(5):
36    print(i)
37    input(":")

1.4 next函数  iter函数

iter函数   迭代器 = iter(可迭代对象)

取出可迭代对象中提供的迭代器

next函数  下一个元素的值 = next(迭代器)

通过迭代器取出下一个元素的值

2. 生成器

是一种特殊的迭代器, 支持迭代器所有的操作

2.1.  生成器表达式

列表推导式[] ——> ()

优点: 节约资源, 不用占用大量空间

在用户需要访问数据的时候 才延迟产生

2.2 生成器函数

含有yield关键字的函数 不再是普通函数 而是生成器函数

 1def myrange(n):
 2    start = 0
 3    while start < n:
 4        # 1 执行到yield会暂停当前代码执行 将后面的值返回到调用生成器代码的地方
 5        # 2 当前生成器代码再次执行时 直接从上次暂停的地方继续往下执行
 6        yield start
 7        return 1000  # 正常情况下 当前生成器会执行3次才会停止迭代 而有return关键字就在第二次停止迭代了
 8        start += 1
 9
10# 生成器函数不是普通函数  调用 生成器函数() 产生生成器对象
11# 执行生成器函数的代码    需要调用next(生成器对象)
12# yield关键字的作用
13gen = myrange(3)
14i = next(gen)
15print(i)
16try:
17    i = next(gen)
18    print(i)
19except StopIteration as e:
20    # 如果需要获取到 生成器最终return的值 需要捕获异常
21    print("接收到了异常数据 %s" % e)
22    pass
23
24# i = next(gen)
25# print(i)
26# print(next(gen))  # 迭代完成 StopIteration
27#

执行生成器并且传参:

 1def myrange(n):
 2    start = 0
 3    while start < n:
 4        # 1 执行到yield会暂停当前代码执行 将后面的值返回到调用生成器代码的地方
 5        # 2 当前生成器代码再次执行时 直接从上次暂停的地方继续往下执行
 6        number = yield start
 7        print("接收到了数据%s" % number)
 8        start += 1
 9
10if __name__ == '__main__':
11    gen = myrange(3)
12    # 生成器支持操作-next函数  迭代器支持的操作
13    i = next(gen)
14    print(i)
15    # 生成器支持操作-send方法  特有
16    i = next(gen)
17    print(i)
18    i = gen.send(100)
19    print(i)
20    # 对比 两种执行生成器的方式的异同
21    # 1 同: 都可以执行生成器代码 并且获取到一个值
22    # 2 异: next不可以发送数据; send可以发送数据;
23    #   第一次调用时send(None), next没有这个要求

2.3 yield关键字作用

1 执行到yield会暂停当前代码执行 将后面的值返回到调用生成器代码的地方
2 当前生成器代码再次执行时 直接从上次暂停的地方继续往下执行

3. 协程

3.1 概念

协程是 用户层面的多任务调度机制        数量可以几十万

进程线程是操作系统层面的多任务机制   数量受操作系统的限制

了解greenlet / yield使用协程

掌握gevent模块的使用

使用yield实现协程:

 1import time
 2
 3
 4def work1():
 5    while True:
 6        print("----work1---")
 7        yield
 8        time.sleep(0.5)
 9
10def work2():
11    while True:
12        print("----work2---")
13        yield
14        time.sleep(0.5)
15
16if __name__ == '__main__':
17    # 创建生成器对象
18    w1 = work1()
19    w2 = work2()
20    while True:
21        next(w1)
22        next(w2)

使用greenlet实现多任务切换:

 1import time
 2import greenlet   # py3环境
 3
 4
 5def work1():
 6    while True:
 7        print("----work1---")
 8        g2.switch()
 9        time.sleep(0.5)
10
11def work2():
12    while True:
13        print("----work2---")
14        g1.switch()
15        time.sleep(0.5)
16
17if __name__ == '__main__':
18    # 创建协程
19    g1 = greenlet.greenlet(work1)
20    g2 = greenlet.greenlet(work2)
21    # 切换到第一个协程执行   手动切换
22    g1.switch()

3.2 gevent模式

创建启动协程   协程对象 = gevent.spawn(入口, 参数列表)

等待协程完成  协程对象.join()

等待多个协程完成  gevent.joinall()

自动切换    from gevent import monkey; monkey.patch_all()

 1from gevent import monkey
 2monkey.patch_all()   # 破解代码 实现自动切换 默认不能自动切换
 3import gevent
 4import time
 5
 6# 能够切换的操作 time.sleep(1) recv  accept  将默认阻塞的操作变为非阻塞的操作
 7
 8def worker(no):
 9    """协程函数"""
10    for i in range(3):
11        print("这是协程 %s %s" % (no, gevent.getcurrent()))
12        time.sleep(1)  # gevent.sleep(1)
13
14def main():
15    # 创建协程 自动运行
16    g1 = gevent.spawn(worker, 1111)
17    g2 = gevent.spawn(worker, 2222)
18
19    # 阻塞等待协程执行完成  - 主要目的是需要让主进程主线程阻塞在这里
20    # g1.join()
21    # g2.join()
22    gevent.joinall([g1, g2])
23
24if __name__ == '__main__':
25    main()

3.3 多任务的选择

资源消耗不关心  要求稳定 用户多进程

资源消耗关心                  多线程或者协程

多任务的网络程序 建议优先使用协程

多任务图片下载器

 1from gevent import monkey
 2monkey.patch_all()   # 启动切换
 3import gevent
 4import urllib.request
 5import time
 6def down_img(url):
 7    """下载指定路径的图片 参数就是图片的链接地址-网址 URL URI"""
 8    # 1 从URL中解析出文件名称
 9    # https://rpic.douyucdn.cn/live-cover/roomCover/2018/08/15/a0c1206ae5da0c02a677506af9f41c40_big.jpg
10    file_name = url[url.rfind("/") + 1: ]
11    print("开始下载图片 %s" % file_name)
12
13    # 2 响应对象<网页数据> 请求    打开网址获取资源对象
14    response = urllib.request.urlopen(url)
15
16    # 3 解析出其中数据 bytes
17    data = response.read()
18
19    # 4 写入文件
20    with open(file_name, "wb") as file:
21        file.write(data)
22
23    print("完成下载图片 %s" % file_name)
24def main():
25    begin = time.time()
26    # 1 创建协程
27    img_list = [
28        "https://rpic.douyucdn.cn/live-cover/roomCover/2018/08/15/a0c1206ae5da0c02a677506af9f41c40_big.jpg",
29        "https://rpic.douyucdn.cn/live-cover/roomCover/2018/10/10/2694864718ef0772c36712cd0e12e3a7_big.jpg",
30        "https://rpic.douyucdn.cn/live-cover/roomCover/2018/10/29/b28ff3c2352b21c766b823db35efc767_big.jpg"]
31    g1 = gevent.spawn(down_img, img_list[0])
32    g2 = gevent.spawn(down_img, img_list[1])
33    g3 = gevent.spawn(down_img, img_list[2])
34    # 2 等待协程执行完成
35    gevent.joinall([g1, g2, g3])
36    end = time.time()
37    print("花费了%f秒" % (end-begin))
38if __name__ == '__main__':
39    main()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值