Python 装饰器

本文章已经生成可运行项目,


1 装饰器介绍

Python 装饰器(Decorator)是一种用于修改或扩展函数或方法行为的工具。它允许在不改变原函数代码的情况下,动态地添加功能。装饰器本质上是一个闭包函数,接受一个函数作为参数并返回一个新的函数。

2 装饰器的主要作用

作用描述
代码复用将通用功能(如日志记录、权限检查、性能测试等)封装到装饰器中,避免重复代码。
增强函数功能在不修改原函数的情况下,添加额外功能。
简化代码通过装饰器将横切关注点(如日志、缓存等)与核心逻辑分离,使代码更清晰。

3 装饰器的常见应用场景

场景描述
日志记录自动记录函数的调用信息。
权限验证在函数执行前检查用户权限
缓存缓存函数的结果,避免重复计算。
性能测试测量函数的执行时间。
事务管理在数据库操作中自动管理事务。

4 装饰器基本语法

装饰器的构建有两种方式:传统语法和语法糖。

语法糖(Syntactic Sugar)是指在编程语言中添加的某种语法特性,它并不改变语言的功能,但能使代码更易读、更简洁,从而提高开发效率和代码的可维护性。语法糖通常通过编译器或解释器转换为等价的标准语法,因此在运行时不会带来性能损失。

4.1 传统方式

def log_decorator(func):
    def log_inner(*args, **kwargs):
        print("方法调用前")
        result = func(*args, **kwargs)
        print("方法调用后")
        return result

    return log_inner


def test(content):
    print(content)

# 创建闭包对象,将要装饰的函数对象作为参数传入
t = log_decorator(test)
# 调用
t("hello world")

4.2 语法糖

语法糖的形式一般常用

# log_decorator 为外部函数
def log_decorator(func):   
	# log_inner 为内部函数
    def log_inner(*args, **kwargs):
        print("方法调用前")
        result = func(*args, **kwargs)
        print("方法调用后")
        return result
	# 返回内部函数对象
    return log_inner


@log_decorator
def test(conetnt):
    print(conetnt)

test("hello world")

注意:装饰函数只能有一个参数,也就是外包函数只能传入一个参数,内部函数没有要求,一般与装饰函数的参数一致。

通过传统函数,可以反推处语法糖的用法:

  • 将被装饰的函数作为参数传入到装饰函数中
  • 内部函数通过引用外部函数变量,并将自身形参传传递函数变量
  • 将内部函数作为对象返回

5 使用案例

5.1 无参无返回

def log_decorator(func):
    def log_inner(*args, **kwargs):
        print("方法调用前")
        func(*args, **kwargs)
        print("方法调用后")

    return log_inner

@log_decorator
def test():
    print("hello world")

if __name__ == '__main__':
    # # 传统方式
    # # 创建闭包对象,将要装饰的函数对象作为参数传入
    # t = log_decorator(test)
    # # 调用
    # t()

    # 语法糖
    test()

5.2 有参无返回

def log_decorator(func):
    def log_inner(content):
        print("方法调用前")
        func(content)
        print("方法调用后")

    return log_inner

@log_decorator
def test(content):
    print(content)

if __name__ == '__main__':
    # # 传统方式
    # # 创建闭包对象,将要装饰的函数对象作为参数传入
    # t = log_decorator(test)
    # # 调用
    # t("hello world")

    # 语法糖
    test("hello world")

5.3无参有返回

def log_decorator(func):
    def log_inner():
        print("方法调用前")
        return func()

    return log_inner


@log_decorator
def test():
    return "hello world"


if __name__ == '__main__':
    # # 传统方式
    # # 创建闭包对象,将要装饰的函数对象作为参数传入
    # t = log_decorator(test)
    # # 调用
    # print(t("hello world"))

    # 语法糖
    print(test())

5.4 有参有返回

def log_decorator(func):
    def log_inner(content):
        print("方法调用前")
        return func(content)

    return log_inner


@log_decorator
def test(content):
    return content


if __name__ == '__main__':
    # # 传统方式
    # # 创建闭包对象,将要装饰的函数对象作为参数传入
    # t = log_decorator(test)
    # # 调用
    # print(t("hello world"))

    # 语法糖
    print(test("hello world"))

5.5 可变参数

def log_decorator(func):
    def log_inner(*args, **kwargs):
        print("方法调用前")
        func(*args, **kwargs)
        print("方法调用前后")

    return log_inner


@log_decorator
def test(*args, **kwargs):
    for arg in args:
        print(arg, end=' ')
    for key, value in kwargs.items():
        print((key, value), end=' ')


if __name__ == '__main__':
    # # 传统方式
    # # 创建闭包对象,将要装饰的函数对象作为参数传入
    # t = log_decorator(test)
    # # 调用
    # t("hello world", "python", name="Java", age=20)

    # 语法糖
    test("hello world", "python", name="Java", age=20)

5.6 多个修饰器的使用

def log_decorator(func):
    def log_inner(*args, **kwargs):
        print("日志记录中……")
        func(*args, **kwargs)

    return log_inner

def cache_decorator(func):
    def cache_inner(*args, **kwargs):
        print("数据缓存中……")
        func(*args, **kwargs)

    return cache_inner


@log_decorator
@cache_decorator
def test(*args, **kwargs):
    for arg in args:
        print(arg, end=' ')
    for key, value in kwargs.items():
        print((key, value), end=' ')


if __name__ == '__main__':
    # # 传统方式
    # # 创建闭包对象,将要装饰的函数对象作为参数传入
    # cache = cache_decorator(test)
    # log = log_decorator(cache)
    # # 调用,log 对象离调用对象最近,所以先返回
    # log("hello world", "python", name="Java", age=20)

    # 语法糖
    test("hello world", "python", name="Java", age=20)

多个装饰器同时使用,如果是传统方式调用,则由内向外进行装饰,也就是根据返回对象离函数最近的返回,说白了,最后是谁调用的及返回的。

cache = cache_decorator(test)
log = log_decorator(cache)
# 调用,log 对象离调用对象最近,所以先返回
log("hello world", "python", name="Java", age=20)

语法糖方式调用,则是从上往下先调用。

@log_decorator
@cache_decorator

两者的返回结果一致,如下:

日志记录中……
数据缓存中……
hello world python ('name', 'Java') ('age', 20) 

5.7 带参数的装饰器

修饰器本身也可以接受参数。此时需要定义一个返回修饰器的函数。

def param_decorator(n):
    def log_decorator(func):
        def log_inner(*args, **kwargs):
            print("日志记录中……")
            for i in range(n):
                print(f"日志记录{i + 1}")
                func(*args, **kwargs)
                print()

        return log_inner

    return log_decorator

@param_decorator(3)
def test(*args, **kwargs):
    for arg in args:
        print(arg, end=' ')
    for key, value in kwargs.items():
        print((key, value), end=' ')


if __name__ == '__main__':
    # # 传统方式
    # # 创建闭包对象,将要装饰的函数对象作为参数传入
    # p = param_decorator(3)
    # t = p(test)
    # # 调用
    # t("hello world", "python", name="Java", age=20)

    # # 语法糖
    test("hello world", "python", name="Java", age=20)

5.8 类装饰器

def func_method(cls):
    def inner_method(self):
        return "这是装饰器返回的函数"

    cls.ext_method = inner_method
    return cls

@func_method
class TestClass:
    pass


if __name__ == '__main__':
    # # 传统方式调用
    # # 装饰 TestClass类,返回修改后的类
    # test = func_method(TestClass)
    # # 实例化对象
    # t = test()
    # print(t.ext_method())

    # 语法糖
    test = TestClass()
    print(test.ext_method())
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值