【Python】装饰器的使用(Decorator)


前言

装饰器(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返回值是Nonesay_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()

输出结果为:

在这里插入图片描述


总结

以上就是装饰器常用场景,装饰器的使用多数为校验或记录执行记录,常常用在登录鉴权,或数据校验场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值