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()