此文与其他文章不同,仅为个人学习笔记使用,编写过程中难免有错误,望各位读者提出,共同进步。
装饰器,迭代器,生成器是python开发过程中重要的工具,装饰器相较于其他两个来说较难以理解。
通俗的来说是用来为其他区函数增加其他功能的,同时遵循一个基本原则:开放封闭原则:开放指对拓展功能开放,封闭指对修改源代码是封闭的。
1.1
简简单单先定义一个函数:
def index(x, y): print("index %s,%s" % (x, y)) index(111, 222)
不难看出结果应该是:
index 111,222
当我们想为这段代码添加一个运行时间的代码,但又不想破坏其本身的完整型
想到利用time方法可以实现:
import time def index(x,y): start = time.time() time.sleep(3) print("index %s,%s" %(x,y)) stop = time.time() print(stop - start) index(111,222)
输出:
index 111,222 3.0005669593811035
1.2
不出意外,运行时间是能统计出来,但是我们破坏了代码的完整性。这算是个失败案例,但是我们想到可以在函数内调用其他函数去实现:定义一个新的函数去接收index;运行1.1函数实际上就是在调用index(111,222),那我们想到在调用阶段为其添加统计时间功能
def index(x,y): print("index %s,%s" % (x, y)) start = time.time() time.sleep(3) index(111,222) stop = time.time() print(stop - start)
输出:
index 111,222 3.000377655029297
结果也是合情合理,但是我们想到在开发过程中 应该会重复调用多次1.1的函数,所以为了代码的简洁性,我们把调用阶段包装成一个函数:
import time def index(x,y): print("index %s,%s" % (x, y)) def timee(x,y): start = time.time() time.sleep(3) index(x,y) stop = time.time() print(stop - start) timee(1,2)
输出:
index 1,2 3.0007386207580566
此时不管有多少需求 我只需要运用time(x,y)函数来掉用。
1.3这时候,我们将index参数写活,用*args,**kwargs进行定义,这样可以优化需求:
import time def timee(*args,**kwargs): start = time.time() index(*args,**kwargs) stop = time.time() print(stop - start) def index(x, y, z): time.sleep(3) print("index %s,%s,%s" % (x, y, z)) timee(1,2,3)
输出:
index 1,2,3 3.0002880096435547
定义阶段:timee(*args,**kwargs):中timee如何调用 就怎么原封不动的index。这是看上去很完美,实际上还有很大的缺陷:timee函数只能为index函数做装饰,如果有其他函数还需要另外重写,不符合代码的简洁性,我们想到可以用嵌套函数在外层把函数装饰参数,
import time def index(x,y,z): time.sleep(3) print("index %s,%s,%s" % (x,y,z)) def outter(func): def timee(*args,**kwargs): start = time.time() func (*args,**kwargs) stop = time.time() print(stop - start) return timee index= outter(index) index(1,2,3)
1.4当我们相想为多个函数添加运行时间功能时,那么另外定义一个就好了:
import time def index(x,y,z): time.sleep(3) print("index %s,%s,%s" % (x,y,z)) return 456 def home (name): time.sleep(2) print("welcome %s come to home page"%name) return 123 def outter(func): # func = index # 的内存地址 def timee(*args,**kwargs): # timee怎么定义的,怎么给index调用,原封不动 start = time.time() func (*args,**kwargs) stop = time.time() print(stop - start) #return 123 #此时才算有timee的返回值 return timee # 是属于outter的返回值 home = outter(home) index= outter(index) r = index(1,2,3) res = home('egon') print('返回值--》',res) print('返回值--》',r)
输出:
index 1,2,3 3.01408314704895 welcome egon come to home page 2.0104966163635254 返回值--》 None 返回值--》 None
我们可以看出想要为home添加功能,不需要改变原有的整体,只用添加相关代码即可,
但是如果我们想为成千上万个函数增加统计运行时间,俺么每次一次代码就需要定义一行,造成了代码冗余,那么@outter这个语法糖可以解决这个问题
1.5@装饰器
import time def outter(func): # func = index # 的内存地址 def timee(*args,**kwargs): # timee怎么定义的,怎么给index调用,原封不动 start = time.time() func (*args,**kwargs) stop = time.time() print(stop - start) # return 123 #此时才算有timee的返回值 return timee # 是属于outter的返回值 @outter #index= outter(index) # home = wrapper的内存地址 def index(x,y,z): time.sleep(3) print("index %s,%s,%s" % (x,y,z)) return 456 @outter #home = outter(home) # f = 当初那个timee函数的内存地址 def home (name): time.sleep(2) print("welcome %s come to home page"%name) return 123 r = index(1,2,3) res = home('egon') print('返回值--》',res) print('返回值--》',r) print(home)
输出:
index 1,2,3 3.007416009902954 welcome egon come to home page 2.0053913593292236 返回值--》 None 返回值--》 None <function outter.<locals>.timee at 0x00907808>
1.6
我们发现打印home时,home的属性已经被指向timee,这肯定不是我们所想要的,functools功能帮我们解决了这个问题:
from functools import wraps def outter(func): @wraps(func) def wrapper(*args,**kwargs): res = func(*args,**kwargs) return res wrapper.__name__ = func.__name__# 手动将原函数的属性赋值给wrapper函数 return wrapper @outter# index= outter(index) def index(x,y): '''这是主页功能''' print(x,y) index.__name__ = 'index' print(index.__name__) print(help(index))
这里我们把index.__name__指向了index 因此帮助文档展示的就是index的注释
输出:
index Help on function index in module __main__: index(x, y) 这是主页功能 None
1.7
有参装饰器:如果装饰器本身需要参数,那么就编写一个返回装饰器的高阶函数:
import functools def log(text): def decorator(func): @functools.wraps(func)#在定义wrapper()的前面加上@functools.wraps(func)即可。 def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw)#返回now函数的打印值 return wrapper return decorator @log('execute') def now(): print('2013-12-25') return 1 print(now())#wrapper
此时,now() = log('execute')now()
输出:
execute now(): 2013-12-25 1
1.8叠加多个装饰器的加载,运行分析:
def deco1(func1): def wrapper1(*args, **kwagrs): res1 = func1(*args, **kwagrs) print("第一个装饰器") return res1 return wrapper1 def deco2(func2): def wrapper2(*args, **kwagrs): print("第二个装饰器") res2 = func2(*args, **kwagrs) return res2 return wrapper2 def deco3(x): def outer3(func3): def wrapper3(*args, **kwagrs): print("第三个装饰器") res3 = func3(*args, **kwagrs) return wrapper3 return outer3 @deco1 @deco2 @deco3(111) def index(x, y): print('from index %s,%s' % (x, y)) print(index(1, 2))
加载顺序应该是自下而上:outer3-->index = outer3(index) -->index = wrapper3的内存地址
index = deco2(wrapper3的内存地址)-->index = wrapper2的内存地址
index = deco1(wrapper2的内存地址)-->index = wrapper1的内存地址
此时打印index(1, 2)返回的应该是 wrapper1的内存地址
执行顺序与加载顺序相反
输出:
第二个装饰器 第三个装饰器 from index 1,2 第一个装饰器 None
迭代器是迭代取值的工具,迭代是一个重复的工具,每次重复都是基于上一次的结果而继续的,单纯的重复并不是迭代。能把多个值循环取出来的类型有:列表,字符串,元组,字典,集合,文件。
先简单列出一个while循环取值:
count =['e','q','f'] q = 0 while q<len(count): print(count[q]) q+=1
上述取值的当时只是适用于有索引的数据类型。
2.1可迭代对象:
但凡内置有__iter__方法的都称之为可迭代的对象,那么我们考察一下上述类型是否为可迭代对象。
s1 ='' print(s1.__iter__()) l = [] print(l.__iter__()) d = {'a':1} print(d.__iter__()) t = (1,) print(t.__iter__()) set1 = {1,2,3} print(set1.__iter__()) with open ("C:/Users/wxl/Desktop.111.txt",mode = "w") as f: pass print(f.__iter__())
输出:列表,字符串,元组,字典,集合,文件均是可迭代对象
<str_iterator object at 0x01DEE6E8> <list_iterator object at 0x01DEE6E8> <dict_keyiterator object at 0x01E048C0> <tuple_iterator object at 0x01DEE6E8> <set_iterator object at 0x01E0D4C8> Traceback (most recent call last): File "C:/Users/wxl/Desktop/笔记.py", line 2260, in <module> print(f.__iter__()) ValueError: I/O operation on closed file.
由此可见:
2.3迭代器对象:调用可迭代对象下的.__iter__方。法会将其转换成迭代器对象
以列表为例:
d = {'a':1,'b':2,"c":3}#调用可迭代对象下的.__iter__方。法会将其转换成迭代器对象 res = d.__iter__() while True: try: print(res.__next__()) except StopIteration: break
输出:
a b c
利用迭代器取值:
d = {'a':1,'b':2,"c":3}#调用可迭代对象下的.__iter__方。法会将其转换成迭代器对象 c = d.__iter__() print(c.__next__()) print(c.__next__())
这里的c是一个迭代器对象,迭代器的内置方法:next:得到迭代器下一个值
输出:
a b
可迭代对象与迭代器对象:
可迭代对象:可以转换为迭代器对象的对象:同时内置有.__iter__方法
迭代器对象:内置有.__next__方法且内置有.__iter__方法
迭代器对象.next:得到迭代器下一个值
迭代器对象.iter:得到迭代器本身
2.4for循环工作原理:
先写一个简单的for循环:
d = [12,2,3] for i in d: print(i)
我们在用迭代器实现一下:
d = [12,2,3] c = d.__iter__() print(c.__next__()) print(c.__next__()) print(c.__next__())
二者输出都是:
12 2 3
那么上述代码中在运行一次print(c.next())会产生什么呢?
Traceback (most recent call last): File "C:/Users/wxl/Desktop/笔记.py", line 2356, in <module> print(c.__next__()) StopIteration
可以看出运行一次就读取一行信息。若把值取干净,若再次调用则会抛出异常。想要再次运行的话重新再定义一次迭代器对象就好了。
接下来解释一下for循环工作原理:
1.d.iter()得到一个迭代器对象
2.迭代器对象.next()会拿到一个返回值,然后将该返回值赋值给i
3.往复循环步骤2,知道抛出异常,佛如循环会捕捉异常然后结束循环
缺点:
1.取值不如索引方便,
2.是一次性用具。
自定义迭代器:在函数内有yield关键字,调用函数指挥返回一个生成器
def func(): print("第一次") yield 1 print("第2次") yield 2 print("第3次") yield 3 print("第4次") res = func() print(res)
输出:
<generator object func at 0x031A33E0>
看出func是generator(生成器)generator不会把所有保存在内存中,而是一边计算一边调用,
res.__iter__() q = res.__next__() print(q)#1 print(q)#1
只调用一次去多次打印迭代器对象得到多次重复的值:
第一次 1 1 1
在python中为了让q = res.next()书写更方便,简化成了next(res)