一、装饰器基础认知

1. 为什么要使用装饰器

装饰器是 Python 优雅的语法糖,能够在程序运行阶段,动态给函数、类拓展额外功能。核心优势:

  1. 通用功能可封装复用,避免重复代码编写
  2. 不修改原有函数源码、不改变函数调用方式,就能新增逻辑
  3. 适配日志打印、性能耗时统计、权限校验、缓存处理、事务操作等高频业务场景

装饰器本质依旧是普通函数,通过@函数名语法绑定目标函数,快速实现功能增强。

2. 装饰器核心定义

  • 装饰:给原有对象追加新业务逻辑
  • :封装好的工具函数核心特性:开闭原则,对原有代码封闭修改,对功能拓展开放。可装饰对象:普通函数、类方法、类。分类:无参装饰器有参装饰器,底层依赖名称空间、函数嵌套、闭包、函数对象四大知识点。

二、无参装饰器实现原理

统计函数运行耗时为例,循序渐进理解装饰器成型过程。

1. 基础原生写法

直接手动统计单个函数执行时间,耦合性极高,无法复用。

import time

def index():
    time.sleep(3)
    print('Welcome to the index page')
    return 200

# 手动统计耗时
start_time = time.time()
index()
stop_time = time.time()
print(f'run time is {stop_time - start_time}')

2. 函数传参写法

将函数当作参数传入,实现统计复用,但改动了函数调用格式,不符合设计规范。

python

运行

import time

def wrapper(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print(f'run time is {stop_time - start_time}')

def index():
    time.sleep(3)
    print('Welcome to the index page')

# 调用方式被修改
wrapper(index)

3. 闭包基础装饰器

利用闭包保存外层函数状态,还原原有调用方式,基础装饰器雏形完成。

python

运行

import time

def timer(func):
    def wrapper():
        start_time = time.time()
        res = func()
        stop_time = time.time()
        print(f'run time is {stop_time - start_time}')
        return res
    return wrapper

def index():
    time.sleep(3)
    print('Welcome to the index page')

# 闭包赋值调用
index = timer(index)
index()

4. 兼容任意参数的装饰器

上述写法无法适配带参函数,使用*args **kwargs接收任意位置、关键字参数,适配所有函数。

python

运行

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print(f'run time is {stop_time - start_time}')
        return res
    return wrapper

@timer
def home(name):
    time.sleep(2)
    print(f'Welcome to the home page {name}')

home('kevin')

5. 语法糖标准使用

@装饰器名是 Python 专用语法糖,等价于函数名 = 装饰器(函数名),紧贴函数上方书写。

python

运行

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print(f'run time is {stop_time - start_time}')
        return res
    return wrapper

# 语法糖装饰
@timer
def index():
    time.sleep(1)
    print('Welcome to the index page')
    return 200

index()

6. 通用装饰器固定模板

日常开发直接套用该模板即可,前后拓展自定义逻辑。

python

运行

def decorator(func):
    def wrapper(*args, **kwargs):
        # 函数执行前逻辑
        print("函数执行前")
        # 调用原函数
        result = func(*args, **kwargs)
        # 函数执行后逻辑
        print("函数执行后")
        return result
    return wrapper

@decorator
def add(a, b):
    return a + b

print(add(1, 2))

三、多层装饰器嵌套

1. 双层装饰器叠加

同一个函数可叠加多个装饰器,从上至下绑定,执行顺序由内向外。示例:同时实现登录校验 + 耗时统计

python

运行

import time

# 登录认证装饰器
def login_auth(func):
    def auth(*args, **kwargs):
        username = input('username:').strip()
        password = input('password:').strip()
        if username == 'kevin' and password == '123':
            print('登录成功')
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名密码错误')
    return auth

# 耗时统计装饰器
def all_time(func1):
    def time1(*args, **kwargs):
        start_time = time.time()
        res = func1(*args, **kwargs)
        stop_time = time.time()
        print(f'功能执行了{stop_time - start_time}秒')
        return res
    return time1

# 双层装饰
@all_time
@login_auth
def index():
    print('执行完成')

index()

2. 多层装饰器执行顺序

装饰器加载顺序从下往上函数调用顺序从上往下,未调用函数仅加载装饰器,不执行内部逻辑。

python

运行

def outter1(func1):
    print('加载了outter1')
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def outter2(func2):
    print('加载了outter2')
    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def outter3(func3):
    print('加载了outter3')
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
    return wrapper3

@outter1
@outter2
@outter3
def index():
    print('from index')

index()

输出顺序

plaintext

加载了outter3
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index

四、装饰器元数据修复

被装饰后的函数会丢失原函数名、文档注释等属性,使用functools.wraps修复函数原生信息。

python

运行

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('函数执行前置操作')
        result = func(*args, **kwargs)
        print('函数执行后置操作')
        return result
    return wrapper

@my_decorator
def say_hello(name):
    """打招呼工具函数"""
    print(f'Hello, {name}!')

# 保留原函数属性
print(say_hello.__name__)
print(say_hello.__doc__)

五、业务实战:登录状态校验装饰器

全局记录登录状态,单次登录后后续函数无需重复验证。

python

运行

is_login = {'is_login':False}

def login_auth(func):
    def login(*args, **kwargs):
        if is_login['is_login']:
            print('已登录,直接放行')
            return func(*args, **kwargs)
        username = input('username:').strip()
        password = input('password:').strip()
        if username == 'kevin' and password == '123':
            print('登录成功')
            is_login['is_login'] = True
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或者密码错误')
    return login

@login_auth
def user_center():
    print('进入个人中心')

user_center()
user_center()

六、有参装饰器

在无参装饰器外层再嵌套一层函数,用于接收自定义参数,语法糖调用时传入参数即可。示例:自定义函数重复执行次数

python

运行

def repeat(count):
    # 内层标准装饰器
    def decorator_func(func):
        def wrapper_func(*args, **kwargs):
            for _ in range(count):
                res = func(*args, **kwargs)
            return res
        return wrapper_func
    return decorator_func

# 传入装饰器参数
@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet('Alice')

七、总结

  1. 装饰器本质:嵌套函数 + 闭包,不改动源码拓展功能
  2. 无参装饰器:三层结构外层接收函数-内层执行逻辑-返回内层函数
  3. 通用参数:*args **kwargs兼容所有形式函数传参
  4. 多层装饰:加载自下而上,调用自上而下
  5. 元数据修复:@wraps(func)保留函数原生属性
  6. 有参装饰器:外层新增函数接收自定义参数,四层嵌套结构
  7. 常用场景:耗时统计、登录鉴权、日志记录、重复执行、缓存拦截

更多推荐