Python内部:装饰器
Source: Dev.to
介绍
让我们拆解抽象层,从头开始构建装饰器,使用堆分配、闭包和时间复杂度作为指引。
如果你来自 C++ 或 Java 等静态、编译型背景,Python 装饰器可能会感觉像黑魔法。你在函数上方加上 @login_required,它立刻拥有了认证逻辑。再加上 @app.route("/"),它立刻变成了一个 Web 端点。
之所以感觉像魔法,是因为它隐藏了复杂性。但作为工程师,我们知道 魔法 只是我们尚未理解的代码。
在本文中,我们将揭开 Python 元编程的神秘面纱。我们不仅要学习 如何 使用装饰器,还要理解使其成为可能的内存机制,构建一个可投入生产的工具箱,并利用它们从根本上改变函数的算法复杂度。
第 1 部分:机制(不允许使用魔法)
在编写装饰器之前,我们需要了解原材料。在 Python 中,使装饰器得以实现的机制是 一等函数 和 闭包。
1. 函数只是堆上分配的对象
- 在 C 中,函数是
.text段中的指令块。 - 在 Python 中,函数是一个完整的对象(
PyObject结构),驻留在堆上。
因为它是对象,你可以:
- 将它赋值给变量,
- 将它作为参数传递给其他函数,
- 从另一个函数返回它。
把函数当作数据处理的能力是元编程的基石。
2. 引擎:闭包
如果你来自 C++,你会知道局部变量在函数的栈帧被弹出时就会消亡。Python 则不同。当内部函数引用了外层作用域的变量时,Python 编译器会注意到并 提升 该变量——把它从普通的栈项变成堆上的 cell 对象。
即使外层函数已经返回,内部函数仍然保留对该 cell 对象的引用。这种“被记住的环境”称为 闭包。
闭包是一个函数,即使在外层函数执行完毕后,仍然记得其封闭作用域中的变量。
装饰器完全依赖闭包来记住它们包装的 哪个 函数。
第 2 部分:构建模式
装饰器本质上非常简单:它是一个接受函数作为输入并返回新函数作为输出的函数。
手动装饰(原始模式)
# The decorator factory
def my_decorator(target_func):
# The wrapper closure retains access to 'target_func'
def wrapper():
print(">>> Before execution")
target_func() # Calling the original function
print(">> Before execution
执行核心逻辑…
注意: 在生产环境中,优先使用 Python 内置的 functools.lru_cache,而不是自行实现,因为它能够处理缓存边界,防止内存泄漏。
总结
装饰器并非魔法。它们是 一等函数 与 闭包 的优雅应用。通过理解 Python 如何在堆上处理作用域和对象生命周期,你就获得了修改行为、注入逻辑以及干净、明确地优化性能的能力。