python 装饰器
一.装饰器概念与语法
python装饰器本质上就是一个嵌套函数,装饰器的返回值也是一个函数对象。简写:装饰器是以一个一函数为参数,返回值为函数的函数
语法:在需要装饰的类或者函数上面写@+函数名称。
二.装饰器的作用
装饰器可以在不更改原功能代码情况下,可以扩展新的功能。
开放封闭原则:
1.以实现的功能,不要去修改其内部(被装饰的函数或类)代码(封闭原则)
2.对功能的扩展(装饰器的扩展)是开放的(开放原则)
三.装饰器的方法
1.函数(闭包函数)
1)函数内部嵌套函数
2)外层函数的返回值是内层函数的函数名
3)内层嵌套函数对外层有一个非全局变量的引用
2.类装饰器(__call__)
1)__call __()方法是将实例成为一个可调用对象(即callable对象),同时不影响实例的构造,但可以改变实例的内部值。
四.代码演示
1.闭包形式的装饰器
1)闭包函数:符合上面三个条件
1 def func():
2 a=10
3 def func_01():
4 print(a)
5 return func_01
2)装饰器:装饰函数
1 def func(fu): #外层函数的第一个参数类型为函数
2 def func_02():
3 print("新功能")
4 fu()
5 return func_02
6
7 @func #func_1=func(func_1)
8 def func_1():
9 print("已经实现的功能")
10 if __name__ == "__main__":
11 func_1()
装饰器原理:
- 当@装饰器装饰的时候,会自动调用装饰器函数,然后把被装饰的函数当作参数传入进去,==》fu = func_1
- 装饰器的返回值,被原来被装饰的函数名接收。func_1 = func_02
调用被装饰的函数:
- 因为被装饰的函数指向的func_02,那么此时就是直接调用func_02,func_02内部又调用了fu(fu实际上此时的指向是被装饰的函数func_01)
流程:@func就相当于func(func_1),所以就是调用func()函数,然后return func_02(返回func_02也就是相当于调用func_02()),那么会先打印print("新功能"),然后接下来再调用fu()函数(也就是func_1()函数),然后就是打印print("已经实现的功能"),然后就结束了。
3)装饰器:装饰类
1 def func(fu): #外层函数的第一个参数类型为函数
2 def func_02():
3 print("新功能")
4 return fu() #这里记得要加return ,要不然你下面 t=func_1() 返回就是一个None了,这里就是一个坑
5 return func_02
6
7 @func #func_1=func(func_1)
8 class func_1():
9 def __init__(self):
10 print("已经实现的功能")
11
12 t=func_1()
13 print(t)
4)装饰器:带固定参装饰器
1 def func(fu): #外层函数的第一个参数类型为函数
2 def func_02(a,b): #这里记得也要传入参数,要不然会报错
3 print("新功能")
4 fu(a,b) #这里记得也要传入参数,要不然会报错
5 return func_02
6
7 @func #func_1=func(func_1)
8 def func_1(a,b):
9 print("a+b",a+b)
10
11 func_1(1,2)
5)装饰器:通用装饰器
1 def func(fu): #外层函数的第一个参数类型为函数
2 def func_02(*args,**kwargs):
3 print("新功能")
4 fu(*args,**kwargs)
5 return func_02
6
7 @func #func_1=func(func_1)
8 def func_1(): #你这里可以传入单个参数或者多个参数都可以
9 print("a+b")
10
11 func_1()
2.类装饰器(__call__)
1)类的装饰器写法, 不带参数(先通过构造函数__init __()传入函数;再通过__call __方法重载,并返回一个函数。)
1 class func_02():
2 def __init__(self, func):
3 self.func = func
4 def __call__(self, *args, **kwargs):
5 print('{} is running'.format(self.func.__name__))
6 return self.func(*args, **kwargs)
7
8 @func_02
9 def func_1(): #这里可以传入单个或者多个参数
10 print("运行成功")
11
12 func_1()
装饰器原理:
- 当@装饰器装饰的时候,它会先执行初始化函数内容,就相当于把被装饰的函数当作参数传入进去,==》func = func_1
- 然后在调用__call__函数运行里面的代码后在执行func_1函数里面的代码,然后再返回去。
2)类的装饰器写法, 带参数(先通过构造函数__init __()传入装饰器参数;再通过__call __方法传入被装饰的函数,并返回一个函数)
1 class func_02():
2 def __init__(self, code):
3 self.code = code
4 def __call__(self, func):
5 def func_03(*args, **kwargs):
6 print('{} is running'.format(func.__name__))
7 print('Code: {}'.format(self.code))
8 return func(*args, **kwargs) # 正式调用主要处理函数
9 return func_03
10
11 @func_02(code="123") #传入参数,可以单个或者多个
12 def func_1():
13 print("运行成功")
14
15 func_1()
3.内置装饰器
1)@property:将一个方法变为属性调用。
未添加装饰器@property时,函数类型是一个方法:<class 'method'>
添加装饰器@property时,函数类型是返回值的类型:如,<class 'str'>
2)property对象的setter方法:表示给属性添加设置功能,即可修改属性值。
若未添加设置属性,就设置新值,则会引发错误AttributeError: can't set attribute。
3)property对象的deleter方法:表示给属性添加删除功能
若添加删除属性,就删除属性则会引发错误AttributeError: can't delete attribute
1 """@property装饰器"""
2 class Test_1:
3 def __init__(self, name):
4 self.name = name
5
6 @property # 将函数由方法变为属性
7 def get_name(self):
8 return self.name
9
10 @get_name.setter # 添加设置属性
11 def get_name(self, value):
12 if not isinstance(value, str):
13 raise TypeError("参数应为字符串类型,但实际是%s类型" % type(value))
14 else:
15 self.name = value
16
17 @get_name.deleter # 添加删除属性
18 def get_name(self):
19 del self.name
20
21 test1 = Test_1("long")
22 # 获取get_name类型
23 print(type(test1.get_name)) # 结果: <class 'str'>
24 # 获取get_name属性值
25 print(test1.get_name) # 结果:long
26 # 给get_name属性设置新值:添加设置属性需使用装饰器@property的setter函数;
27 test1.get_name = "赋新值"
28 print(test1.get_name) # 结果:赋新值
29 # 删除get_name属性:删除属性需使用装饰器@property的deleter函数;
30 del test1.get_name
31 print(test1.get_name) # 结果:报错(AttributeError: 'Test1' object has no attribute '_Test1__name'),表示删除属性成功
4.第三方库装饰器
1 from functools import wraps 2 3 def decorate(func): 4 print('running decorate', func) 5 print('running decorate name:', func.__name__) 6 #@wraps(func) #标记代码 7 def decorate_inner(): 8 print('running decorate_inner function', decorate_inner) 9 print("你是:",decorate_inner.__name__) 10 return func() 11 return decorate_inner 12 13 @decorate 14 def func_1(): 15 print('running func_1', func_1) 16 print('你是:', func_1.__name__,"函数") 17 18 if __name__ == '__main__': 19 func_1()
结果:
无启用@wraps(func):
说明:当你没有启用@wraps(func)时候,那么被装饰的函数名称已经被更改了。
启用@wraps(func):
说明:当你启用@wraps(func)时候,那么被装饰的函数名称没有被更改。
五.面试问题
1.请列举装饰器两种实现方式 --------> 闭包函数、类中定义的__call__方法
2.装饰器可以装饰类吗 --------> 可以
3.类可以当装饰器吗 --------> 可以
4.如何实现通用装饰器 ---------> 传参使用不定长参数
5.可以同时被多个装饰器装饰吗 ---------> 可以,优先运行离装饰函数或类近的装饰器,以此往外执行。
六.应用场景
1.可以再外层函数加上时间计算函数,计算函数运行时间。
1 import time 2 def func_1(func): 3 def func_2(): 4 start=time.time() 5 func() 6 time.sleep(1) 7 end=time.time()-start 8 print("运行时间是:",end) 9 return func_2 10 11 @func_1 12 def func_3(): 13 print("运行结束") 14 15 func_3()
2.计算函数运行次数。
3.可以用在框架的路由参数上。
4.插入日志,作为函数的运行日志。
5.事务处理,可以让函数实现事务的一致性,让函数要么一起运行成功,要么一起运行失败。
6.缓存,实现缓存处理。
7.权限的校验,在函数外层套上权限验证的代码,实现权限校验。
8.登录校验
1 def login(func): #外层函数的第一个参数类型为函数
2 def func_02():
3 name=input("请输入用户名:")
4 pwd=input("请输入密码:")
5 if name=="admin" and pwd==1:
6 func()
7 else:
8 print("用户名或密码错误,请重新登录")
9 return func_02
10
11 @login #func_1=login(func_1)
12 def func_1():
13 print("登录成功,欢迎您")
14
15 func_1()