Python装饰器核心机制与实战:从原理到带参验证

林见深
林见深
发布时间:2026-10-14| 7人浏览过
原创

本文深入解析Python装饰器的底层原理,涵盖高阶函数、函数嵌套与闭包三大核心概念。通过计算执行时间与用户登录验证两个实战案例,演示如何编写无参及带参装饰器,确保在不修改源码和调用方式的前提下扩展函数功能。

一、前言

装饰器的本质是一个函数,其主要作用是为其他函数添加额外的功能。

在使用装饰器时,必须遵循以下两个核心原则:

  • 1、不修改被修饰函数的源代码。
  • 2、不修改被修饰函数的调用方式。

装饰器的实现依赖于三个关键要素的组合:高阶函数、函数嵌套以及闭包。

二、高阶函数

高阶函数的定义包含以下要点:

  • 1、函数接收的参数是一个函数。
  • 2、函数的返回值是一个函数名。
  • 只要满足上述任意一个条件,该函数即可被称为高阶函数。

例如,test 函数接收了 foo 作为参数,因此它是高阶函数。


import time
def foo():
    time.sleep(3)
    print("sleep 3s")
 
def test(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print("函数的运行时间是: %s" % (stop_time - start_time))
 
test(foo)

timer 函数返回了一个函数,因此它也是高阶函数。


import time
def foo():
    time.sleep(3)
    print("sleep 3s")
 
def timer(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print("执行时间{}".format(stop_time - start_time))
    return func
foo = timer(foo)
foo()
# 结果: 多运行了一次

三、函数嵌套

在函数内部定义另一个函数时,变量的作用域和生存周期保持不变。


def father(name):
    print("father name: %s" % name)
    def son():
        print("son name: %s" % name)
    son()
father("xu1")
 
# 结果:
#     father name: xu1
#     son name: xu1

四、装饰器

本节将实现一个计算函数执行时间的装饰器,用于统计并打印被装饰函数的运行耗时。


import time
 
def timer(func):  # 实现一个计算函数执行时间的函数作为装饰器,用来计算被装饰函数的执行时间并打出
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("运行时间: %s" % (stop_time - start_time))
    return wrapper
 
# def test():  # 不使用装饰器的同等实现
#     time.sleep(3)
#     print("test sleep 3s")
#
# test = timer(test)  # 返回的是 wrapper 的地址
# test()  # 执行的是 wrapper
 
 
@timer
def test():  # 装饰器的实现
    time.sleep(3)
    print("test sleep 3s")
 
test()  # 执行的是 wrapper
# 结果:
#     test sleep 3s
#     运行时间: 3.000915050506592

4.1 被装饰方法带返回值


import time
 
 
def timer(func):
    def wrapper():
        start_time = time.time()
        res = func()  # 执行被装饰方法
        stop_time = time.time()
        print("运行时间: %s" % (stop_time - start_time))
        return res  # 接受正在调用的方法的返回值,并返回
    return wrapper
 
 
@timer
def test():
    time.sleep(3)
    print("test sleep 3s")
    return "test return ok"
 
 
print(test())  # 执行的是 wrapper
# 结果:
#     test sleep 3s
#     运行时间: 3.0002923011779785
#     test return ok

4.2 被装饰方法带参数


import time
 
 
def timer(func):
    """
        *args:将被修饰方法传入的非关键字参数打包为元组 args
        **kwargs: 将被修饰方法传入的关键字参数打包为字典 kwargs
    """
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)  # *args 拆解元组,按顺序传给被修饰函数; **kwargs:拆解字典
        stop_time = time.time()
        print("运行时间: %s" % (stop_time - start_time))
        return res
    return wrapper
 
 
@timer  # 给test 方法添加计算执行时间的装饰器
def test(name, age):
    time.sleep(3)
    print("name = {}, age = {}".format(name, age))
    return "test return ok"
 
 
# 调用被装饰器装饰的方法
print(test("xu", 100))  # 执行的是 wrapper
# 结果:
#     name = xu, age = 100
#     运行时间: 3.000420331954956
#     test return ok

4.3 验证功能装饰器

假设 index()、home() 和 shopping_car() 三个方法均需要登录后才能访问(若未登录则无法执行内部逻辑)。在正常流程中,用户只需登录一次,后续访问其他方法时无需再次登录。

可以通过 @auth_fun 装饰器来验证用户是否已登录。如果未登录,则提示用户输入账号和密码;若验证通过,则记录当前登录状态,后续其他方法无需再次进行登录验证。


# 用户列表
user_list = [
    {'name': 'xu1', 'passwd': '123'},
    {'name': 'xu2', 'passwd': '123'},
    {'name': 'xu3', 'passwd': '123'},
    {'name': 'xu4', 'passwd': '123'},
]
# 当前登录的用户
current_dic = {"username": None, "login": False}
 
 
# 验证用户是否登录的装饰器
#   如果用户没有登录,让用户输入账号密码,校验通过记录用户状态
def auth_fun(func):
    def wrapper(*args, **kwargs):
        if current_dic["username"] and current_dic['login']:
            res = func(*args, **kwargs)
            return res
        username = input("请输入用户名:")
        pw = input("请输入密码:")
        for u in user_list:
            if u["name"] == username and u["passwd"] == pw:
                current_dic["username"] = username
                current_dic["login"] = True
                res = func(*args, **kwargs)
                return res
        else:
            print("用户没有注册!")
    return wrapper
 
 
@auth_fun
def index():
    print("this is index")
 
 
@auth_fun
def home():
    print("this is home page")
 
 
@auth_fun
def shopping_car():
    print("this is shopping car")
 
 
index()  # 输入用户密码
home()  # index 已经登录,无需在输入
shopping_car()  # index 已经登录,无需在输入
# 结果:
#     请输入用户名:xu1
#     请输入密码:123
#     this is index
#     this is home page
#     this is shopping car

4.4 验证功能装饰器——带参数

当装饰器带有参数时,最简单的应用场景是对被装饰的函数进行区别处理。


# 用户列表
user_list = [
    {'name': 'xu1', 'passwd': '123'},
    {'name': 'xu2', 'passwd': '123'},
    {'name': 'xu3', 'passwd': '123'},
    {'name': 'xu4', 'passwd': '123'},
]
# 当前登录的用户
current_dic = {"username": None, "login": False}
 
"""
    注意:带参数的装饰器会比没有带参数的装饰器多嵌套一层函数(多了auth)
        调用方式是 @auth(auth_type="type1"), 返回 auth_fun,
        也就是说 @auth(auth_type="type1")相当于 @auth_fun
        但是 auth_fun 函数所在的嵌套作用域多了一个 auth_type 的变量
"""
def auth(auth_type="type1"):
    def auth_fun(func):
        def wrapper(*args, **kwargs):
            if auth_type == "type1":
                if current_dic["username"] and current_dic['login']:
                    res = func(*args, **kwargs)
                    return res
                username = input("请输入用户名:")
                pw = input("请输入密码:")
                for u in user_list:
                    if u["name"] == username and u["passwd"] == pw:
                        current_dic["username"] = username
                        current_dic["login"] = True
                        res = func(*args, **kwargs)
                        return res
                else:
                    print("用户没有注册!")
            elif auth_type == "type2":
                print("不用授权直接登录: type = {}".format(auth_type))
                res = func(*args, **kwargs)
                return res
            else:
                print("其他type没有实现")
        return wrapper
    return auth_fun
 
 
"""
    auth_fun = @auth(auth_type="type1") 
    auth_fun 所在的嵌套与将有一个 auth_type 变量
    然后通过 @auth()方法返回的对象注解 index,相当于 @auth_fun 注解index 方法,最后得到 wrapper 对象
"""
@auth(auth_type="type1")
def index():
    print("this is index")
 
 
@auth(auth_type="type2")
def home():
    print("this is home page")
 
 
@auth(auth_type="type3")
def shopping_car():
    print("this is shopping car")
 
 
home()  # 注意:auth_type="type2",这个方法无需登录可以直接执行
index()  # 注意:auth_type="type1",需要登录
shopping_car()  # 注意:auth_type="type3",没有做处理
# 结果:
#     不用授权直接登录: type = type2
#     this is home page
#     请输入用户名:xu1
#     请输入密码:123
#     this is index
#     其他type没有实现

到此这篇关于python基础之装饰器详解的文章就介绍到这了,更多相关python装饰器内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。