本文在书写时,参考了一下几篇博客,在此贴出来,以示尊重:
装饰器必看博客1
装饰器必看博客2
装饰器必看博客3
1、装饰器使用说明。
(1)、Python中的装饰器是一种可以装饰其它对象的工具。
(2)、该工具本质上是一个可调用的对象(callable),所以装饰器一般可以由函数、类
来实现。
(3、)装饰器本身需要接受一个被装饰的对象作为参数,该参数通常为函数、方法、类等对象。
(4)、装饰器需要返回一个对象
,该对象可以是经过处理的原参数对象
、一个包装且类似原参数的对象
,或者返回一个不相干内容(通常不建议使用)
(5)闭包
是装饰器的核心内容。
2.装饰器的常见使用场景。
2.1、函数装饰器
(1)最简单的函数装饰器
接收一个被装饰的对象,不对该对象做任何的补充
def test(obj):
return obj
@test #=>等价于obj=test(obj),相当于函数对象的实例化。
def obj():
print("hello wurenji ")
obj()
输出结果:
hello wurenji
(2)函数装饰器
用于模拟对象的装饰器
–函数装饰器
在上面例子中的装饰器,是直接修改了传入对象;而装饰器最常用的方式却是模拟一个传入对象。即返回一个和原对象相似的对象(即调用接口完全一样的另一个对象),并且该模拟对象是包装了原对象在内的。具体代码如下:
def test(func): # 最简单的函数装饰器,以被装饰的函数对象作为参数
def inner():
print("This is decorate code")#在装饰器内部加上需要为原函数补充的内容。
func()
print("wo ai xiaobao")
return inner#返回的是添加完修饰后的函数对象。不是被修饰的函数。
@test#@test作用:指明了test函数实际修饰的函数obj,调用test(func),并且将调用结果赋值给它装饰的函数(obj)。
def obj():
print("This is obj function")
obj()
结果如下:
This is decorate code
This is obj function
wo ai xiaobao
函数装饰器的实质性探索:将闭包的值赋予了被修饰函数
。
def test(func): # 最简单的函数装饰器,以被装饰的函数对象作为参数
def inner(x,y,u,v):#闭包中含有参数。
print("This is decorate code") # 在装饰器内部加上需要为原函数补充的内容。
func(u,v)
print("wo ai xiaobao")
sum = x+y
print(sum)
return inner
@test # @test作用:将obj函数作为参数传递给了装饰器,并且将test的返回值的引用赋值给了obj。
def obj(a,b):
print("This is obj function")
result=a*b
print("This is obj's result is :{}".format(result))
obj(3,4,5,6)#调用的实质:利用被修饰的函数obj对为obj函数实际添加功能的闭包函数inner中参数(x,y,u,v)的初始化。
obj(3,4,5,6)#调用的实质:利用被修饰的函数obj对为obj函数实际添加功能的闭包函数inner中参数(x,y,u,v)的初始化
。并且将闭包inner的值赋予了被修饰的obj(a,b)函数
。输出结果如下:
This is decorate code
This is obj function
This is obj's result is :30
wo ai xiaobao
7
(3)含有参数的函数装饰器
def deco(msg):#存放装饰器自己的参数。
def inner_deco(func):#装饰器的实现函数,()内的参数为func,表明该闭包的作用是为func函数起到修饰作用。
def new_func(x,y,u,v):#装饰的具体实现.()存放在执行func函数时可能用到的参数以及其他参数,这些参数在调用原func时进行初始化。
print(f'[log] run function {func.__name__}')
func(u,v)
print("This is the deco_func_param_result:{}".format(x*y))
print(f'[log] {msg}')
return new_func
return inner_deco
@deco('wo ai ni ')#装饰器自己的参数必须这样初始化。
def foo(x,y):
print('This is foo')
print("This is foo's result:{}".format(x*y))
foo(3,4,5,6)#3,4是对被引用的闭包中new_func()函数中的x,y的初始化。5,6是对new_func中u v的初始化。
输出结果如下:
[log] run function foo
This is foo
This is foo's result:30
This is the deco_func_param_result:12
[log] wo ai ni
(4)含有不定长参数的装饰器。
[1]、被装饰的函数带有参数。
def deco1(deco_param):#()存放的是装饰器自己的参数
def inner_deco(func):#()存放的是被修饰的函数对象,本质上是闭包的应用。
#new_func的()存放的是被修饰的函数的参数以及其他参数(可有可无),其他参数初始化由func()函数在初始化时一并初始化。
def new_func(func_param):
print(f"This is deco_param_value:{deco_param}.")
func(func_param)
print("The rest of func")
return new_func
return inner_deco
@deco1(90)
def func(param):
print(f"This is func_param_value:{param}")
func(10)
输出结果如下:
This is deco_param_value:90.
This is func_param_value:10
The rest of func
[2]、被修饰的函数带有不定长参数。
被装饰的函数带有不定长参数,借助Python中的*args和**kwargs使被装饰的函数可以支持传入任意参数。
def deco2(deco_param):
def inner(func):
def new_func(u,v,*args,**kwargs):#()里面的参数由实际调用被修饰的func函数进行初始化。
print(f"This is deco_param_value:{u+v}")
func(*args,**kwargs)
print(f'[log] run function {func.__name__}')#获取正在运行的函数的名字。
return new_func
return inner
@deco2("deco_param_value:10")
def obj1(*args,**kwargs):
print(f'The tuple params is {args},Dict params is {kwargs}')
obj1(12,13,34,78,23,x=10,y=19)
输出结果如下:
This is deco_param_value:25
The tuple params is (34, 78, 23),Dict params is {'x': 10, 'y': 19}
[log] run function obj1
分析
:执行obj1(12,13,34,78,23,x=10,y=19)
时,前两个参数12,13传递给new_func函数中的u,v,其余参数作为不定长参数传递给被修饰的fun函数(obj1自身用到的参数。)。
(5)被修饰的函数有返回值
def deco3(deco_param):
def inner(func):
def new_func(*args,**kwargs):
print(f'This is running_function name :{func.__name__}')
func_value=func(*args,**kwargs)
print('This is new add function' )
return func_value#被修饰的函数含有返回值,必须返回。
return new_func
return inner
@deco3('wo ai xiaobao')#注意不能丢弃装饰器的参数
def func0(a,b):
result=a+b
return result
结果输出如下:
This is running_function name :func0
This is new add function
30
分析
: 如果被修饰的函数中有返回值,一定要在装饰器的最内层闭包中将被修饰函数的返回值返回到其外层嵌套函数。等到装饰器的逻辑执行完毕,才返回最终结果。
2.2、类方法装饰器。
类方法装饰器与函数装饰器类似,其作用相同,格式相近。
def outer(obj_func): #obj参数是一个类方法。类方法的装饰器本质上与函数的装饰器一样,就是将待修饰的函数传递给闭包里面的内嵌函数 。
#类方法装饰器与普通装饰器相比就是多了一个self参数,因为类方法本身就有self参数,其他用到的参数在实际调用被修饰的类方法时进行初始化。
def inner(self,x,y):
obj_func(self)#修饰被装饰的类方法,切记self参数和其他参数(如果有的话)不能少。
print("This is add content")
sum=x+y
print(f'This is sum:{sum}')
print(f'This is running_function name :{obj_func.__name__}')
return inner
class Zoo(object):
def __init__(self):
pass
@outer # => zoo = outer(zoo)
def zoo(self):#被装饰的类方法
print('This class method')
foo = Zoo()
print(foo.zoo.__name__)#返回的是被修饰函数所在闭包的名字,此处zoo实际上是装饰器返回值的引用(闭包的返回值的名字)
foo.zoo(3,4)#执行装饰器的返回值的引用,切记不能漏掉装饰器中的参数。
输出结果如下:
inner
This class method
This is add content
This is sum:7
This is running_function name :zoo
2.3、类装饰器。
装饰器除了可以装饰函数、方法之外,还可以装饰类对象。修饰类对象的装饰器称为类装饰器。
类装饰器通过一个含有返回值为类类型的闭包来实现
。
def outer(clss): # 类装饰器,clss为一个类
class Inner():#装饰器修饰的是类对象,所以闭包的返回值为类。该类由被修饰的实例对象来初始化(即该类中
# 需要外传参数时,可通过实例化的对象来完成。)
def __init__(self,a,b,u,v):
self.clss = clss(u,v)#执行被修饰的实例对象。
self.name='zhangsan'
# a,b参数由被修饰的实例对象实例化时进行传递,若果被修饰的类初始化时需要传递参数,也需要在实例化时一并传递进来。
self.number=a+b
#__getattr_为内置方法,当使用点号获取实例属性时,如果属性不存在实例对象就自动调用__getattr__方法,
# 通过该方法来获取被修饰对象中对应的同名属性值。
def __getattr__(self, attr):
return getattr(self.clss, attr)#获取类的某个属性值。
# 当下面的say方法在装饰器中不存在时,装饰器就会通过__getattr__函数调用被修饰的类中的say方法,前提是被修饰的实例对象被执行,
# 并且被修饰的类当中存在同名方法。
def say(self):
print(self.name)
print(self.number)
def abc(self):
print("This is in deco funtion")
return Inner#装饰器的返回类型必须和被修饰的函数一致.
@outer # Zoo = outer(Zoo)
class Zoo():
def __init__(self,x,y):
self.Zoo_sum=x*y
def say(self):
print('hello world!')
def Zoo_print(self):
print(f'This is Zoo_out:{self.Zoo_sum}')
zoo = Zoo(3,5,7,8)#3.5用来初始化装饰器中的类需要的参数.7,8为被修饰的类自己的类初始化时需要的参数。
print(zoo.__class__) # <class '__main__.outer.<locals>.Inner'>
zoo.say() # hello world!
zoo.abc()
zoo.Zoo_print()
print(zoo.name)
print(zoo.number)
输出结果如下:
<class '__main__.outer.<locals>.Inner'>
zhangsan
8
This is in deco funtion
This is Zoo_out:56
zhangsan
8
分析
:在被修饰函数执行时,当调用的方法与装饰器中的方法同名时(本例中为say()方法),会优先调用修饰器中的同名方法。
当在被修饰的类中调用某个方法时,装饰器中不存在该方法时,会通过装饰器中的__getattr__方法
来调用被修饰的类中的同名方法,但是,如果被修饰的类在装饰器中未执行时,调用_getarrt_方法会报错。如下所示:
def outer(clss): # 类装饰器,clss为一个类
class Inner():#装饰器修饰的是类对象,所以闭包的返回值为类。该类由被修饰的实例对象来初始化(即该类中
# 需要外传参数时,可通过实例化的对象来完成。)
def __init__(self,a,b,u,v):
#self.clss = clss(u,v)#不执行被修饰的实例对象。
self.name='zhangsan'
# a,b参数由被修饰的实例对象实例化时进行传递,若果被修饰的类初始化时需要传递参数,也需要在实例化时一并传递进来。
self.number=a+b
#__getattr_为内置方法,当使用点号获取实例属性时,如果属性不存在实例对象就自动调用__getattr__方法,
# 通过该方法来获取被修饰对象中对应的同名属性值。
def __getattr__(self, attr):
return getattr(self.clss, attr)#获取类的某个属性值。
# 当下面的say方法在装饰器中不存在时,装饰器就会通过__getattr__函数调用被修饰的类中的say方法,前提是被修饰的实例对象被执行,
# 并且被修饰的类当中存在同名方法。
# def say(self):
# print(self.name)
#print(self.number)
def abc(self):
print("This is in deco funtion")
return Inner#装饰器的返回类型必须和被修饰的函数一致.
@outer # Zoo = outer(Zoo)
class Zoo():
def __init__(self,x,y):
self.Zoo_sum=x*y
def say(self):
print('hello world!')
def Zoo_print(self):
print(f'This is Zoo_out:{self.Zoo_sum}')
zoo = Zoo(3,5,7,8)#3.5用来初始化装饰器中的类需要的参数.7,8为被修饰的类自己的类初始化时需要的参数。
print(zoo.__class__) # <class '__main__.outer.<locals>.Inner'>
zoo.say() #装饰器中不存在say()方法,所以会通过__getarrt__方法调用被修饰类中的同名方法。
zoo.abc()
zoo.Zoo_print()
print(zoo.name)
print(zoo.number)
结果报错:
RecursionError: maximum recursion depth exceeded while calling a Python object
显示调用类对象失败。未能成功调用被修饰的类中的say()方法。
当装饰器执行了被修饰类且被修饰的类中含有同名方法时,会通过__getarrt_方法调用被修饰的类中的同名方法:
def outer(clss): # 类装饰器,clss为一个类
class Inner():#装饰器修饰的是类对象,所以闭包的返回值为类。该类由被修饰的实例对象来初始化(即该类中
# 需要外传参数时,可通过实例化的对象来完成。)
def __init__(self,a,b,u,v):
self.clss = clss(u,v)#执行被修饰的实例对象。
self.name='zhangsan'
# a,b参数由被修饰的实例对象实例化时进行传递,若果被修饰的类初始化时需要传递参数,也需要在实例化时一并传递进来。
self.number=a+b
#__getattr_为内置方法,当使用点号获取实例属性时,如果属性不存在实例对象就自动调用__getattr__方法,
# 通过该方法来获取被修饰对象中对应的同名属性值。
def __getattr__(self, attr):
return getattr(self.clss, attr)#获取类的某个属性值。
# 当下面的say方法在装饰器中不存在时,装饰器就会通过__getattr__函数调用被修饰的类中的say方法,前提是被修饰的实例对象被执行,
# 并且被修饰的类当中存在同名方法。
#def say(self):
#print(self.name)
# print(self.number)
def abc(self):
print("This is the add function")
return Inner#装饰器的返回类型必须和被修饰的函数一致.
@outer # Zoo = outer(Zoo)
class Zoo():
def __init__(self,x,y):
self.Zoo_sum=x*y
def say(self,u,v):
print('This is in original class')
def Zoo_print(self):
print(f'This is Zoo_out:{self.Zoo_sum}')
zoo = Zoo(3,5,7,8)#3.5用来初始化装饰器中的类需要的参数.7,8为被修饰的类自己的类初始化时需要的参数。
print(zoo.__class__) # <class '__main__.outer.<locals>.Inner'>
zoo.say() # hello world!
zoo.abc()
zoo.Zoo_print()
print(zoo.name)
print(zoo.number)
结果如下:
<class '__main__.outer.<locals>.Inner'>
This is in original class
This is the add function
This is Zoo_out:56
zhangsan
8
2.4、用类作为装饰器。
用来作为装饰器是通过类中的__call__方法来实现的。
用类作为装饰器通常有两种写法:
(1)、通过模拟函数装饰器来实现对被装饰函数的修饰:
class Deco1():
def __call__(self,func):
def new_func(x,y,u,v):
print(f'The running functiong is: {func.__name__}')
print(f'The sum of x and y is:{x+y}')
func(u,v)
return new_func
@Deco1()#将__call__方法返回的引用传递给了foo()函数。
def foo(u,v):
print('This is in original function')
print('The sum of u and v is:{}'.format(u+v))
foo(1,2,3,4)#初始化__call__方法中的参数。
输出结果如下:
The running functiong is: foo
The sum of x and y is:3
This is in original function
The sum of u and v is:7
(2)、直接实现对被装饰的函数的修饰
class Warp(object):
#def __init__(self):
# pass
def __call__(self, obj):
obj.name = 'warp'
print('This is in {} function'.format(obj.__name__))
return obj
@Warp()
def foo():
print('this is foo')
#print(foo.name) # => warp
foo()
print(foo.name)
输出结果如下:
This is in foo function
this is foo
warp
在本例中直接为foo()函数添加了name属性以及额外的print()函数。