Python装饰器原理与简单用法实例分析

认真发呆
认真发呆
发布时间:2026-10-13| 7人浏览过
原创

本文深入解析Python装饰器的工作原理,通过逐步替换演示从基础装饰器到带参装饰器的实现过程。重点讲解@语法糖等价于函数赋值、内嵌包装函数确保每次调用生效,以及多层嵌套处理带参装饰器的执行顺序,帮助开发者彻底理解装饰器机制。

本文通过具体实例详细解析Python装饰器的核心原理及其基础应用方法,旨在为读者提供清晰易懂的参考指南。

在整理装饰器相关知识时,面对内嵌装饰器、带参装饰器等复杂形式,初学者往往感到困惑。然而,一旦理清其内在逻辑,便会发现其本质其实十分简洁。

第一步:从最简单的示例入手

# -*- coding:gbk -*-
'''示例1: 使用语法糖@来装饰函数,相当于"myfunc = deco(myfunc)"
但发现新函数只在第一次被调用,且原函数多调用了一次'''
def deco(func):
  print("before myfunc() called.")
  func()
  print(" after myfunc() called.")
  return func
@deco
def myfunc():
  print(" myfunc() called.")
myfunc()
myfunc()

这是一个最基础的装饰器示例,但存在一个关键问题:当多次调用myfunc()时,装饰器函数仅执行了一次。要理解这一现象,需要掌握解开装饰器机制的关键线索。

代码中的@deco语句与myfunc = deco(myfunc)在功能上完全等价,仅是书写形式的差异。

请务必牢记上述等价关系!

自此开始,只需进行相应的替换操作即可理解。

将@deco替换为myfunc = deco(myfunc)

程序首先执行deco(myfunc),并将返回结果赋值给(需注意:在Python中,函数名本质上是指向函数首地址的指针)。

deco(myfunc)的返回值即为函数myfunc()的地址。

因此,并未发生实质变化,这意味着后续对myfunc()的两次调用,实际上并未真正执行到deco()内部逻辑。

有读者可能会质疑:明明打印了deco()的内容,为何说未调用?这源于对执行时机的误解。该打印语句是在@deco定义阶段执行的。实际运行测试会发现,"myfunc() called."输出三次,多出的那次正是@deco执行myfunc = deco(myfunc)时触发的deco()调用。

第二步:确保装饰器被正确调用

如何解决装饰器未被调用的问题?

# -*- coding:gbk -*-
'''示例2: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
  def _deco():
    print("before myfunc() called.")
    func()
    print(" after myfunc() called.")
    # 不需要返回func,实际上应返回原函数的返回值
  return _deco
@deco
def myfunc():
  print(" myfunc() called.")
  return 'ok'
myfunc()
myfunc()

无需额外解释,沿用第一步的替换逻辑即可。此处稍作补充说明。

将@deco替换为myfunc = deco(myfunc)

程序首先执行deco(myfunc),将返回结果赋值给,此时变为指向函数_deco()的指针。

随后的myfunc()调用,实际执行的是_deco()。

第三步:对带参数的函数进行装饰

推导过程与第一、二步完全一致,此处不再赘述。

# -*- coding:gbk -*-
'''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
  def _deco(a, b):
    print("before myfunc() called.")
    ret = func(a, b)
    print(" after myfunc() called. result: %s" % ret)
    return ret
  return _deco
@deco
def myfunc(a, b):
  print(" myfunc(%s,%s) called." % (a, b))
  return a + b
myfunc(1, 2)
myfunc(3, 4)

第四步:实现带参数的装饰器

# -*- coding:gbk -*-
'''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
def deco(arg):
  def _deco(func):
    def __deco():
      print("before %s called [%s]." % (func.__name__, arg))
      func()
      print(" after %s called [%s]." % (func.__name__, arg))
    return __deco
  return _deco
@deco("mymodule")
def myfunc():
  print(" myfunc() called.")
@deco("module2")
def myfunc2():
  print(" myfunc2() called.")
myfunc()
myfunc2()

此类装饰器的解析逻辑相同,核心仍在于替换操作。

将@deco("mymodule")替换为myfunc = deco("mymodule")(myfunc )

请注意,此处deco后跟随了两个括号。

有读者询问其具体含义。

逻辑很简单:先执行deco("mymodule"),返回结果为_deco。

再执行_deco(myfunc),得到最终结果__deco。

因此,myfunc = __deco成立。

至此,问题彻底解决。

如需了解更多Python相关内容,可参考本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《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 算法库。