装饰器本质上就是一个python函数,它可让其余函数在不须要作任何代码变更的前提下增长额外功能,装饰器的返回值也是一个函数对象。它常常用于有切面需求的场景,好比:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,咱们就能够抽离大量与函数功能自己无关的雷同代码并继续重用。归纳讲,装饰器的做用就是为依据存在的对象添加额外的功能。
html
在python里面函数就是一个变量,函数名就是一个变量,这个函数名里面存的是这个函数的内存地址,它把函数体放到内存里,在调用的时候从函数名里面的这个内存地址找到函数体而后运行这个函数。前面的博客说函数的时候,说过函数名后面加上小括号就是调用这个函数,若是只写这个函数名的话,打印一下就是这个函数的内存地址。python
1 def test(): 2 print('Hello Word') 3 4 5 print(test) # 打印函数的内存地址 #<function test at 0x100662e18> 6 test() # 调用函数
1 import time 2 def bar(): 3 time.sleep(3) 4 print('in the bar !!') 5 6 def test1(func): 7 start_tiem = time.time() 8 func()# run bar() 9 stop_time = time.time() 10 print('the func run time is %s' %(stop_time-start_tiem)) 11 test1(bar)
1 def fun(): 2 print('in fun ') 3 def bar(): #函数的嵌套 4 print('in the bar ') 5 bar() 6 fun() 7 #--------------------------# 8 name = "Baylor" 9 def change_name(): 10 11 name = "Baylor2" 12 def change_name2(): 13 name = "Baylor3" 14 print("第3层打", name) 15 16 change_name2() # 调用内层函数 17 print("第2层打印", name) 18 19 change_name() 20 print("最外层打印", name)
嵌套函数中的做用域顺序-由内而外的寻找符合要求的参数json
但每一次都须要重新对 test1 进行函数赋值,咱们只须要经过装饰器的特殊书写方式,在须要增长新功能的函数上增长“@装饰器函数名”就能够完成装饰器的使用 缓存
1 def timer(func): 2 def deco():#函数的嵌套 timer(test1) func = test1 3 start_time = time.time() 4 func() #run test1 5 stop_time = time.time() 6 print(' the func time is %s'%(stop_time - start_time)) 7 return deco #高阶函数- 返回值中包含函数名 8 9 @timer # test1 = timer(test1) 10 def test1(): 11 time.sleep(3) 12 print('in the test1') 13 @timer 14 def test2(): 15 time.sleep(2) 16 print('in the test2') 17 18 #test1 = timer(test1) 19 test1()# --->deco 20 test2() 21 print(timer(test1)) #打印test1 内存地址
因此咱们说:高阶函数+嵌套函数 =>装饰器 ,咱们经过高阶函数+函数嵌套实现了以上的装饰器,他符合不改变调用方式,为函数增长新功能。数据结构
问题:被装饰的函数若是有参数呢?app
1 def w1(func): 2 def inner(arg): 3 # 验证1 4 # 验证2 5 # 验证3 6 return func(arg) 7 return inner 8 9 @w1 10 def f1(arg): 11 print ('f1') 12 13 14 def w1(func): 15 def inner(arg1,arg2,arg3): 16 # 验证1 17 # 验证2 18 # 验证3 19 return func(arg1,arg2,arg3) 20 return inner 21 22 @w1 23 def f1(arg1,arg2,arg3): 24 print ('f1')
问题:能够装饰具备处理n个参数的函数的装饰器?函数
1 def w1(func): 2 def inner(*args, **kwargs): 3 # 验证1 4 # 验证2 5 # 验证3 6 print(*args,**kwargs) 7 8 return func(*args, **kwargs) 9 return inner 10 11 @w1 12 def f1(arg1, arg2, arg3): 13 print('f1') 14 15 f1(1,2,3)
若是要装饰的函数带有参数时,由于你也不知道到底被装饰的函数会传什么参数,因此可使用可变参数和关键字参数来接收全部的参数性能
1 import time 2 def timer(func): 3 def deco(*args,**kwargs):#函数的嵌套 timer(test1) func = test1 4 start_time = time.time() 5 func(*args,**kwargs) #run test1 6 stop_time = time.time() 7 print(' the func time is %s'%(stop_time - start_time)) 8 return deco #高阶函数 9 10 @timer # test1 = timer(test1) 11 def test1(): 12 time.sleep(3) 13 print('in the test1') 14 @timer # test2 = timer(test2) = deco test2(name) = deco(name) 15 def test2(name,arg): 16 time.sleep(2) 17 print('in the test2 %s %s',name,arg) 18 19 test1()# --->deco 20 test2('Baylor',28)
1 import time 2 user,passwd = 'jinyu','abc123' 3 def auth(auth_type): 4 def outer_wrapper(func): 5 print('auth func:',auth_type) 6 def wrapper(*args,**kwargs): 7 print('------',auth_type) 8 if auth_type =='local': 9 username = input('Username:') 10 password = input('Password') 11 if user == username and passwd == password: 12 13 print('\033[31;1mUser has password authentication\033[0m') 14 # res = func(*args,**kwargs) 15 res = func(*args,**kwargs) #1,2 16 print('-----afte authentication-----',res) 17 res.append(3) 18 print(res) 19 return res,'方法已运行完毕' # 结果 20 else: 21 print('\033[32;1m Invalid username or password\033[0m') 22 elif auth_type =='ldap': 23 print('哈哈哈哈哈哈!!!ldap') 24 return wrapper 25 return outer_wrapper 26 27 def index(): 28 print('welcome to index page!') 29 @auth(auth_type = 'local')#home = wraapper 30 def home(): 31 print('welcome to home page!') 32 return [1,2] 33 @auth(auth_type = 'ldap') 34 def bbs(): 35 print('welcome to bbs page!') 36 37 index() 38 home() 39 # bbs()
三层函数造成了终结装饰器,经过装饰器所传的参数可以进行装饰器功能判断,提供了更好的扩展性 测试
1 a = [i*2 for i in range(10)] #列表生成式 3行代码一行搞定 2 print(a) 3 4 a = [] 5 for i in range(10): 6 a.append(i*2) 7 print(a)
1 a =(i * 2 for i in range(10)) #若是要打印generator中的元素须要借助next方法 须要循环才能取出 2 # a =[i * 2 for i in range(10)] # list的元素咱们能够一个个打印出, 3 print(a) #<generator object <genexpr> at 0x10228f2b0> 4 print(next(a)) 5 print(next(a)) 6 print(a.__next__())
7 print(a.__next__())
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print(b) 5 a, b = b, a + b # t(b,a+b) a=t[0]-1 b=t[1]-2 a+b等同于 t的下标相加 6 n = n + 1 7 return 'done' # 无用 8 9 10 fib(10) 11 12 # 若是把这个改进成 yield 生成器,经过 __next__调用 只须要 把print(b) 改为 yield b 便可 13 temp = [] 14 15 16 def fib(max): 17 n, a, b = 0, 0, 1 18 while n < max: 19 yield b 20 a, b = b, a + b # t(b,a+b) a=t[0]-1 b=t[1]-2 a+b等同于 t的下标相加 21 n = n + 1 22 return 'done' # 无用 23 24 x = fib(10) 25 print(x) 26 print(x.__next__()) 27 print(x.__next__()) 28 print(x.__next__()) 29 print(x.__next__())
1 #匿名函数就是不须要显式的指定函数 2 3 # 这段代码 4 def calc(n): 5 return n * n 6 7 print(calc(10)) 8 9 # 换成匿名函数 10 calc = lambda n: n * n 11 print(calc(10)) 12 #你也许会说,用上这个东西没感受有毛方便呀, 。。。。呵呵,若是是这么用,确实没毛线改进,不过匿名函数主要是和其它函数搭配使用的呢,以下 13 #x=[1, 5, 7, 4, 8] 14 res = map(lambda x: x * 2, [1, 5, 7, 4, 8]) #map会根据提供的函数对指定序列作映射。 15 for i in res: 16 print(i)
#匿名函数只能处理比较简单的处理逻辑,只能写简单的表达式,不能写循环 判断,好比三元运算符
json是一种全部语言中都通用的key-value数据结构的数据类型,很像python中的字典,json处理使用json模块,json模块有下面经常使用的方法:ui
1 import json 2 3 dic = {"name": "niuniu", "age": 18} 4 print(json.dumps(dic)) # 把字典转成json串 5 fj = open('a.json', 'w') 6 print(json.dump(dic, fj)) # 把字典转换成的json串写到一个文件里面 7 s_json = '{"name":"niuniu","age":20,"status":true}' 8 print(json.loads(s_json)) # 把json串转换成字典 9 10 # 先建立b.json文件 运行运行此代码*{"name": "chenjianguo", "age": 18}* 11 fr = open('b.json', 'r') 12 print(json.load(fr)) # 从文件中读取json数据,而后转成字
内置函数详细介绍 https://docs.python.org/3/library/functions.html?highlight=built#ord
1 import time 2 def consumer(name): 3 print('%s 我准备吃包子了'%name) 4 while True: 5 baozi = yield #保存当前状态返回, 6 print('包子[%s]来了,被[%s]吃了'%(baozi,name)) 7 # c = consumer('houzi') 8 # c.__next__() 9 #b = '韭菜馅' 10 #c.send(b) # 调用yield 并传值 11 #c.__next__() 12 def producer(name): 13 c = consumer('A') #生成 14 c2 = consumer('B') 15 c.__next__() 16 c2.__next__() 17 print('开始准备作包子了。。。。。。') 18 for i in range(10): 19 time.sleep(1) 20 print('作了2个包子!!!!') 21 c.send(i) 22 c2.send(i) 23 24 producer('houzi')
分享一下,须要用类里面的__call__方法,__call__方法就是能够把这个实例当成一个函数来调用,若是正常使用类里面的方法的话,实例方法要先实例化类,而后才能调用,静态方法、类方法则须要用类名或者实例化以后的对象来调用,而实例化以后的这个对象,是不能被调用的,__call__方法就是把这个实例变成一个能够调用的对象,也就是说实例化以后,这个对象就能够和一个普通函数同样被调用。
1 class Foo: 2 def __call__(self, *args, **kwargs): 3 print('call....') 4 def test(self):# 5 print('test....') 6 if __name__ == '__main__': 7 t = Foo()#实例化类 8 t.test()#正常调用实例方法 9 t()#直接调用实例化以后的对象
理解了上面的以后,就可使用class来写一个装饰器了,计算程序的运行时间,固然思想和之前用函数写装饰器是同样的
1 class Fuck(object): 2 def __init__(self, func): 3 self.func = func 4 def __call__(self, *args, **kwargs): 5 import time 6 start_time = time.time() 7 res = self.func(*args, **kwargs) 8 end_time = time.time() 9 print('the function "%s" run time is %s' % (self.func.__name__, 10 (end_time - start_time))) 11 return res 12 @Fuck 13 def run(name): 14 import time 15 time.sleep(1) 16 return 'sb_%s' % name 17 print(run('hyf'))