前言
装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
一、装饰器是什么?
装饰器是一个函数,在python中它有着特殊的写法,通过@func写在执行函数前面,此时这个执行函数就会通过参数去执行这个装饰器。所以更加简单的理解就是整个只是个函数嵌套的过程。
二、装饰器执行场景
装饰器的执行场景:
- 日志记录: 记录函数的调用信息
- 性能测试: 记录函数的执行时间
- 权限校验: 校验函数的登录权限等
- 函数校验: 校验函数执行的合理性
1. 嵌套函数
先把装饰器拆开成普通的函数嵌套,如下
def my_decorator(func):
print("before excuse fuc...")
func()
print("end excuse fuc...")
def say_hello():
print("hello")
my_decorator(say_hello)
执行结果:
这里装饰器函数接收一个参数参数,然后在内部嵌套执行
注意: 函数A调用装饰器函数B,是指装饰器函数B接收函数A为参数,然后调用执行装饰器函数B,防止颠倒这个执行对象。
2. 装饰器语法
假如不对装饰器函数修改,直接调用装饰器:
def my_decorator(func):
print("before excuse fuc...")
func()
print("end excuse fuc...")
@my_decorator
def say_hello():
print("hello")
say_hello()
执行结果如下:
显然,装饰器的执行过程不是简单的套用嵌套,而是函数替换, 实际的装饰器函数应该如下:
say_hello = my_decorator(say_hello)
如果像上面,装饰器函数my_decorator
返回值是None
,say_hello
函数替换后就是None
,造成后续执行失败。所以通常的装饰器写法,需要中间嵌套 wrapper
函数,然后将其返回。
def my_decorator(func):
def wrapper():
print("before excuse fuc...")
func()
print("end excuse fuc...")
return wrapper
@my_decorator
def say_hello():
print("hello")
say_hello()
执行结果如下:
注意:函数wrapper()和wrapper, 是两个不同的概念,say_hello是指函数对象(函数指针),它不会被立即执行,所以返回函数的时候应该返回的是wrapper,在外面被调用执行。
3. 带参数的函数调用装饰器
假如函数带参数:
def decorator_with_args(func):
def wrapper(*args, **kwargs):
print(f"函数 {func.__name__} 被调用,参数: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"函数 {func.__name__} 执行完毕")
return result
return wrapper
@decorator_with_args
def add(a, b):
return a + b
print(add(3, 5))
参数a, b 传给add函数,在装饰器里面wrapper相当于add方法,*args 捕获所有位置参数,得到 args = (3, 5),**kwargs 捕获所有关键字参数(本例中没有,故为空),执行结果返回add的计算结果。
4. 带参数的装饰器
假如装饰器带参数:在原有的装饰器上再嵌套一层用来传递参数。
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello {name}")
greet("Alice")
5. 类作为装饰器
类作为装饰器,执行__call__
函数:say_hello函数作为参数传递给类CountCalls,此时say_hello是类CountCalls的实例化对象,开始执行init函数,初始化_init_函数(self.func = say_hello),完成函数替换执行__call__返回的函数。
class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"调用次数: {self.num_calls}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
6. 内置装饰器
@classmethod是类方法,他可以直接别类调用,同时可以访问和修改类属性。对于@staticmethod静态方法,他不需要实例化,可以直接被类名调用,同时不可以访问类属性,或实习化对象。下面代码的name属性,通过@name.setter来赋值(复制同时做校验),和@property装饰器把方法变成属性被对象访问。
class MyClass:
def __init__(self):
self._name = None
@classmethod
def class_method(cls):
print("这是一个类方法")
@staticmethod
def static_method():
print("这是一个静态方法")
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise ValueError("name should be str")
self._name = value.title()
if __name__ == '__main__':
MyClass.class_method()
MyClass.static_method()
obj = MyClass()
obj.name = "andy"
print(obj.name)
7. 多个装饰器
对于函数可以调用多个装饰器,多个装饰器都可以起到校验作用,校验顺序如下,且在装饰器的函数同步执行。
def decorator1(func):
def wrapper():
print("装饰器1 - 前")
func()
print("装饰器1 - 后")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器2 - 前")
func()
print("装饰器2 - 后")
return wrapper
@decorator1
@decorator2
def my_function():
print("原函数")
my_function()
输出结果为:
总结
以上就是装饰器常用场景,装饰器的使用多数为校验或记录执行记录,常常用在登录鉴权,或数据校验场景。