Python进阶系列
Python进阶-网络编程-01
Python进阶-网络编程-02
Python进阶-网络编程-03
Python进阶-多任务编程-01
Python进阶-多任务编程-02
Python进阶-多任务编程-03
Python进阶-正则表达式
Python进阶-数据库编程-01
Python进阶-数据库编程-02
Python进阶-数据库编程-03
Python进阶-数据库编程-04
Python进阶-数据拷贝问题
Python进阶-模块导入问题
Python进阶-miniWeb框架
文章目录
8.1. 自定义迭代对象、迭代器
-
具体实现步骤
- 自定义列表类:
MyList(object):
- 初始化方法
__iter()__
方法,对外提供迭代器addltem()
方法,用来添加数据
- 自定义迭代器类:
MyListlterator(object):
- 初始化方法
- 迭代器方法
__iter__()
- 获取下一个元素值的方法
__next__()
- 自定义列表类:
-
代码示例:
class MyList(object): def __init__(self): # 定义实例属性,保存数据 self.items = [] # 对外提供迭代器 def __iter__(self): mylist_iterator = MyListIterator(self.items) return mylist_iterator def addItem(self,data): self.items.append(data) class MyListIterator(object): def __init__(self,items): # 定义实例属性,保存MyList类传递过来的items self.items = items # 记录迭代器迭代的位置 self.current_index = 0 def __iter__(self): pass def __next__(self): # 1.判断当前的下标是否越界 # 1)根据下标获取对应的元素值 # 2)下标位置+1 # 3)返回小标对应的数据 if self.current_index < len(self.items): data = self.items[self.current_index] self.current_index += 1 return data # 2.如果越界,直接抛出异常 else: # raise 用于主动跑抛出异常 raise StopIteration if __name__ == '__main__': # 创建自定义列表对象 mylist = MyList() mylist.addItem("张飞") mylist.addItem("关于") mylist.addItem("班长") # iter(要遍历的对象),mylist是自己定义的列表对象, # 类中提供了iter()的可迭代器,表示对象可以迭代 mylist_iterator = iter(mylist) value = next(mylist_iterator) print(value) value = next(mylist_iterator) print(value) value = next(mylist_iterator) print(value)
8.2. 迭代器案例:斐波那契数列
-
自定义一个迭代器 Fibnacci()
__iter__()
__nxet__()
-
代码示例:
class Fibnacci(object): def __init__(self,num): # 定义实例属性,保存生成的列数 self.num = num # 保存第一列和第二列 self.a = 1 self.b = 1 # 记录位置的下标 self.current_index = 0 def __iter__(self): return self def __next__(self): # 1.判断列数是否超过生成的总列数 # 定义变量,保存a的值 # a=b,b=a+b # 当前列数+1 # 返回值 if self.current_index < self.num: data = self.a self.a,self.b = self.b,self.a+self.b self.current_index += 1 return data else: raise StopIteration # 如果超出范围,报错 if __name__ == '__main__': # 创建迭代器的对象 fib_iterator = Fibnacci(5) for value in fib_iterator: print(value)
8.3. 生成器-基本使用
-
概念:特殊的迭代器(按照一定的规律生成数列)
-
生成器创建方式:
-
列表推导式
data_list2 = (x*2 for x in range(10)) print(data_list2) # 通过next获取下一个值 value = next(data_list2) print("------->",value)
-
函数中使用
yield
# 使用yield 创建了一个生成器 def test(): yield 10 n = test() print("n =",n) value = next(n) print(value)
-
8.4. 生成器案例:斐波那契数列
-
思路:
1.创建一个生成器,实现斐波那契数列
定义变量保存第一列和第二列的值定义变量保存当前生成的位置 循环生成数据,条件(当前的列数<总列数) 保存a的值 修改a、b的值 (a b,b a+b) 返回a的值yield
2.定义变量保存生成器
next(生成器)得到下一个元素值 -
yield的作用
- 充当
return
作用 - 保存程序的运行状态 ,并且暂停程序执行
- 当
next()
的时候,可以继续唤醒程序从yield
位置继续向下执行
- 充当
-
程序执行过程
8.5. 生成器-使用事项
-
return的作用:可以结束生成器的运行
-
send()的作用:
生成器.send(值)
fib.send(1) xxx = yield data # xxx = 1
8.6. 协程-yield
-
协程:在不开辟新的线程的基础上,实现多个任务。协程是一个特殊的生成器
-
代码示例:
import time def work1(): while True: print("正在执行work1…………") yield time.sleep(0.5) def work2(): while True: print("正在执行work2…………") yield time.sleep(0.5) if __name__ == '__main__': # 定义生成器 w1 = work1() w2 = work2() while True: next(w1) next(w2)
8.7. 协程-greenlet
-
greenlet实现协程:greenlet是一个第三方的模块,自行调度的微线程
-
具体步骤:
-
导入模块
from greenlet import greenlet
-
创建任务 work1() work2()
def work1(): while True: print("正在执行work1…") time.sleep(0.5) # 切换到第二个任务 g2.switch()
def work2(): while True: print("正在执行work2…") time.sleep(0.5) # 切换回第一个任务 g1.switch()
-
创建greenlet对象
g1 = greenlet(work1) g2 = greenlet(work2)
-
手动switch任务
g1.switch()
-
-
代码示例:
import time from greenlet import greenlet def work1(): while True: print("正在执行work1…………") time.sleep(0.5) # 切换到第二个任务 g2.switch() def work2(): while True: print("正在执行work2…………") time.sleep(0.5) # 切换回第一个任务 g1.switch() if __name__ == '__main__': # 创建greenlet对象 g1 = greenlet(work1) g2 = greenlet(work2) g1.switch()
8.8. 协程-gevent
-
gevent实现协程:gevent也是第三方库,自动调度协程,自动识别程序中的耗时操作
-
使用步骤:
-
导入模块
import gevent
-
指派任务
# gevent.spawn(函数名,参数1,参数2…) g1 = gevent.spawn(work1) g2 = gevent.spawn(work2)
-
join()
让主线程等待协程执行完毕后再退出g1.join() g2.join()
-
-
gevent不能识别耗时操作的问题
- 替换
time.sleep()
->gevent.sleep()
- 替换
- 打补丁:让gevent识别time.sleep()
-
打猴子补丁过程
-
导入模块:
from gevent import monkey
-
破解所有:
monkey.patch_all()
-
-
猴子补丁的作用:
- 在运行时替换方法、属性等
- 在不修改第三方代码的情况下增加原来不支持的功能
- 在运行时为内存中的对象增加
patch
而不是在磁盘的源代码中增加
-
代码示例:
from gevent import monkey monkey.patch_all() import time import gevent def work1(): while True: print("正在执行work1…………",gevent.getcurrent()) gevent.sleep(0.5) def work2(): while True: print("正在执行work2…………",gevent.getcurrent()) gevent.sleep(0.5) if __name__ == '__main__': # 指派任务 gevent.spawn(函数名,参数1,参数2) g1 = gevent.spawn(work1) g2 = gevent.spawn(work2) g1.join() g2.join()
8.9. 进程、线程、协程区别
-
进程、线程、协程的定义
- 进程:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
- 线程:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
- 协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候·恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
-
进程资源分配的基本单位,线程CPU调度的基本单位,协程单线程执行多任务
-
选择问题
进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
缺陷:多个进程之间通信成本高,切换开销大。线程:密集/O任务(网络1/O,磁盘1/0,数据库I/O)使用多线程合适。
缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。协程:当程序中存在大量不需要CPU的操作时(IO),适用于协程;
8.10. 并行下载器
-
案例实现思路:
-
定义要下载的图片路径
-
调用文件下载的函数,专门下载文件
下载函数
-
根据url地址请求网络资源
-
在本地创建文件,准备保存
-
读取网络资源数据
-
把读取的网络资源写入到本地文件中
-
做异常捕获
-
-
打开网址对应的资源
urllib.request.urlopen(网址)
-
joinall()
批量把协程添加join
joinall( [协程的列表] ) gevent.joinall ([ gevent.spawn(download_img, img_url1, "1.gif"), gevent.spawn(download_img, img_url2, "2.gif"), gevent.spawn(download_img, img_url3, "3.gif") ])
-
代码示例
from gevent import monkey monkey.patch_all() import gevent import urllib def main(): img_url1 = "http.mpitc.cn" img_url2 = "http.mpitc.cn" img_url3 = "http.mpitc.cn" gevent.joinall([ gevent.spawn(download_img, img_url1, "1.gif"), gevent.spawn(download_img, img_url2, "2.gif"), gevent.spawn(download_img, img_url3, "3.gif") ]) def download_img(img_url,file_name): try: # 1.根据url地址请求网络资源 resource_data = urllib.request.urlopen(img_url) # 2.在本地创建文件,准备保存 with open(file_name,"wb") as file: while True: # 3.读取网络资源数据 file_data = resource_data.read(1024) if file_data: # 4.把读取的网络资源写入到本地文件中 file.write(file_data) else: break # 5.做异常捕获 except Exception as e: print("文件%s下载失败!" % file_name) else: print("文件%s下载成功!" % file_name) if __name__ == '__main__': main()
8.11. 案例:协程版Web服务器
-
思路:
-
导入模块 gevent
-
创建协程 gevent.spawn()
gevent.spawn(self.request_handler,new_client_socket,ip_port)
-
打补丁
from gevent import monkey monkey.patch_all()
-