概述
介绍
Python中的装饰器是一种可调用的对象(它可以是函数、或实现__call__的类),它将被装饰对象(函数、或类)作为参数传入,再经过执行所需要的处理后,返回另一个可调用对象。装饰器的本质就是把一个可调用对象变成另一个可调用对象。装饰器可以由以下多种用法。
- 函数装饰器装饰函数
- 函数装饰器装饰类
- 函数装饰器装饰方法(类中定义的函数)
- 实现__call__的类装饰器装饰函数
- 实现__call__的类装饰器装饰类
- 实现__call__的类装饰器装饰方法(类中定义的函数)
函数装饰器
本节介绍常见的用一个函数装饰器,装饰另一个函数的情况。
装饰器的功能为修改、加工被装饰函数的功能,为了解装饰器的简单原理,我们一步步描述如何修饰函数对象。
- 函数赋值
Python中函数也是一种对象,它可以赋值给变量。
def func():
print("hello func.")
def changed_func():
print("hello changed func.")
func()
## output: hello func.
func = changed_func
func()
## output: hello changed func.
从上面的代码可以看到,我们可以通过函数赋值给变量的方法,使func可以调用不同的实际函数。
- 将函数作为参数
因为函数亦是对象,因此可以作为函数参数传入。
def decorate(func):
return changed_func
def func():
print("hello func.")
def changed_func():
print("hello changed func.")
func()
## output: hello func.
func = decorate(func)
func()
## output: hello changed func.
我们可以通过将func传到decorate函数中,让decorate函数帮忙返回修改后的函数changed_func,此时func()和第一种直接赋值的方法类似,具备了新的功能,成为了新的函数changed_func。
- 装饰器方法
将上小节中func传入decorate函数赋值的方法,改造成Python的装饰器用法。
def decorate(func):
def changed_func(*args, **kwargs):
print("hello changed func.")
return func(*args, **kwargs)
return changed_func
@decorate
def func():
print("hello func.")
func()
""" output
hello changed func.
hello func.
"""
通过Python的语法糖@,可以在func定义时对函数进行修饰,使func变为新的函数,这里将changed_func函数定义到了decorate函数里使装饰器函数更加内聚紧凑,如果像前面两节独立定义changed_func的方式,也不影响功能。
前面两种修改函数对象的方法都是在程序运行时完成的,而装饰器作为Python的一种语法,就像第2小节decorate赋值的方法: func = decorate(func)一样,对func完成了赋值操作,只不过它是在模块导入的时候就已经完成了这个操作。
因此@decorate可以理解为func = decorate(func)。装饰器decorate返回的是一个闭包,当调用func()时,相当于调用了changed_func(),关于闭包可以参考Python3中的命名绑定、解析与函数闭包
装饰器执行的顺序
我们通过新建一个Python文件”test.py“,分别通过导入模块和运行test.py文件观察装饰器执行顺序。
test.py文件:
def decorate(func):
print("decorate func: %s" % func)
return func
@decorate
def func1():
print("hello func1.")
@decorate
def func2():
print("hello func2.")
if __name__ == '__main__':
func1()
func2()
当在某一个模块中使用from test import *的导入模块语句时,程序的输出为:
decorate func: <function func1 at 0x10d44a950>
decorate func: <function func2 at 0x10e785378>
而直接运行 python3 test.py时程序的输出为:
decorate func: <function func1 at 0x10aa37840>
decorate func: <function func2 at 0x10aa37950>
hello func1.
hello func2.
可以看到装饰器decorate在模块导入时就已经执行,并将被装饰函数:func1、func2修饰。而在运行的执行func1和func2时,func1和func2已经被装饰器修饰,因此可以理解为装饰器是一种静态修饰函数功能的方法。
装饰器保留函数元信息
def decorate(func):
def changed_func(*args, **kwargs):
print("hello changed func.")
return func(*args, **kwargs)
return changed_func
@decorate
def func():
print("hello func.")
print(func.__name__)
""" output
changed_func
"""
我们可以看到,当装饰器decorate装饰了函数func时,func相当于变成了changed_func,因此在打印func.__name__时显示的是函数changed_func的名字。我们可以通过Python标准库中的functools.wraps装饰器,使changed_func函数保留原始函数func的信息。
from functools import wraps
def decorate(func):
@wraps(func)
def changed_func(*args, **kwargs):
print("hello changed func.")
return func(*args, **kwargs)
return changed_func
@decorate
def func():
print("hello func.")
print(func.__name__)
func.__wrapped__()
""" output
func
hello func.
"""
通过wraps装饰器,func函数拥有了__wrapped__属性,该属性就是不被装饰器修饰的func本身,因此调用该函数只是调用了func函数而没有被装饰器修饰。
带参数的装饰器
from functools import wraps
def decorate(arg1):
print(arg1)
def _decorate(func):
@wraps(func)
def changed_func(*args, **kwargs):
print("hello changed func.")
return func(*args, **kwargs)
return changed_func
return _decorate
@decorate("test")
def func():
print("hello func.")
func()
""" output
test
hello changed func.
hello func.
"""
@decorate('test)相当于func = decorate(‘test’)(func)
除此之外还有一些函数装饰器的用法不过多介绍,其实只要记住装饰器的本质就可以了,参考以下链接:
- 可自定义属性的装饰器
- 带可选参数的装饰器
- 将装饰器定义为类的一部分
函数装饰器装饰类和方法
为类提供装饰器,起到了一些元类的作用,可以重写或修改类的行为,具体可以参考:使用装饰器扩充类的功能。
为方法提供函数装饰器可以参考:为类和静态方法提供装饰器。
它的缺点时不能运用到继承中,下面通过元类的方式简单实现可以继承的装饰器。元类相关信息可以参考:Python3元类
class DecoratorInheritMeta(type):
def __new__(metacls, *args, **kwargs):
_, bases, namespace = args
need_inherit_map = {k: v for k, v in namespace.items() if hasattr(v, '__wrapper__')}
for base in bases:
for k, v in getattr(base, '__need_inherit_map__', {}).items():
if k in namespace.keys():
namespace[k] = v.__wrapper__(namespace[k])
else:
need_inherit_map[k] = v
cls = super().__new__(metacls, *args)
cls.__need_inherit_map__ = need_inherit_map
return cls
class DecoratorInheritMetaMixin(metaclass=DecoratorInheritMeta):
@staticmethod
def inherit_decorator(decorator):
def decorate(func):
wrapper = decorator(func)
wrapper.__wrapper__ = decorator
return wrapper
return decorate
def decorate(func):
def changed_func(*args, **kwargs):
print("hello changed func.")
return func(*args, **kwargs)
return changed_func
class A(DecoratorInheritMetaMixin):
@DecoratorInheritMetaMixin.inherit_decorator(decorate)
def func(self):
print("hello func A.")
class B(A):
def func(self):
print('hello func B.')
A().func()
B().func()
""" output
hello changed func.
hello func A.
hello changed func.
hello func B.
"""
实现__call__的类装饰器装饰函数
除了函数可以作为装饰器,任何实现了__call__方法的类也可以作为装饰器,当类装饰器装饰函数时,该函数则变为装饰器的对象,拥有对象的相关属性和方法。例如:
from functools import wraps
class Decorate():
def __init__(self, func):
wraps(func)(self)
self.func = self.__wrapped__
self.ncalls = 0
def __call__(self, *args, **kwargs):
self.ncalls += 1
return self.func(*args, **kwargs)
def total_calls(self):
return self.ncalls
@Decorate
def func():
print("hello func.")
func()
func()
func()
print('Total call times by func: ', func.total_calls())
""" output
hello func.
hello func.
hello func.
Total call times by func: 3
"""
通过以上代码可以实现计算函数func执行的次数,装饰器类Decorate在装饰func时,生成对象(执行__init__方法),这里调用’wraps(func)(self)’,目的和之前的@wraps(func)一样,保留func的元信息,只不过这里的self相当于之前函数装饰器时的内部函数。通过实现__call__方法,可以把Decorate的对象变为可调用对象,这里执行被装饰函数func以及其他工作。func由于被Decorate修饰,具有Decorate对象的方法,因此可以通过调用func.total_calls方法,实现得到func被调用总次数的功能。通过使用类装饰器的方法,不但可以实现函数装饰器闭包的功能, 还可以更清晰地看到被装饰后拥有哪些属性和方法,可以使装饰器更加灵活。
装饰器类装饰方法和类
当装饰器类修饰方法时需要特别注意。按照装饰器的核心,装饰器将被装饰的可执行对象变为装饰器类的对象,而由于函数这个对象存在__get__方法(为了类对象的方法访问,在类中定义的函数本身是个描述器),因此我们也需要为装饰器类实现__get__方法,实现对象方法调用的描述器功能。
import types
from functools import wraps
class Decorate():
def __init__(self, func):
wraps(func)(self)
self.func = self.__wrapped__
self.ncalls = 0
def __call__(self, *args, **kwargs):
self.ncalls += 1
return self.func(*args, **kwargs)
def __get__(self, instance, owner):
if instance is None:
return self
return types.MethodType(self, instance)
def total_calls(self):
return self.ncalls
class Test:
@Decorate
def func(self):
print("hello func.")
t = Test()
t.func()
t.func()
t.func()
print('Total call times by func: ', t.func.total_calls())
""" output
hello func.
hello func.
hello func.
Total call times by func: 3
"""
__get__方法实现了描述器的功能,为了使在调用对象t.func()时实现绑定方法的返回,如不实现该函数,则在调用t.func()不会绑定func中的self。
至于使用装饰器类直接装饰类的场景还没有遇到过,而且当用这种方法时,被装饰的类会变为装饰器类的对象,在继承时会出现问题,因此这种方法暂不考虑。
最后
以上就是踏实自行车为你收集整理的Python3装饰器介绍函数装饰器函数装饰器装饰类和方法实现__call__的类装饰器装饰函数装饰器类装饰方法和类的全部内容,希望文章能够帮你解决Python3装饰器介绍函数装饰器函数装饰器装饰类和方法实现__call__的类装饰器装饰函数装饰器类装饰方法和类所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复