Python装饰器深入解析:从基础到高级应用

键盘旁的猫
键盘旁的猫
发布时间:2026-10-15 | 5人浏览过
原创

本文深入解析Python装饰器的核心原理与实战用法。从无需修改原代码即可扩展功能的角度出发,逐步演示如何构建通用装饰器、处理带参函数、支持不定长参数以及组合多个装饰器。通过具体代码案例,阐明装饰器的工作机制、执行顺序及常见错误排查,帮助开发者掌握这一Python高级特性。

Python装饰器本质上是一个返回值为函数的特殊函数,其主要作用是在不修改原函数源代码的前提下,为现有函数添加新的功能。这种机制极大地提升了代码的可维护性和复用性。

首先通过一个简单示例来引入概念:


def say():
 print('Nice day')

say()
# 这个函数的输出为:
Nice day

假设当前代码输出Nice day,现在需要在该输出之前额外打印一行****************,期望效果如下所示:

**************** 

Nice day

常规思路下,可以直接修改原函数代码来实现这一需求:


def say():
 print('****************')
 print('Nice day')

say()

然而,如果后续发现需求变更,需要恢复原状,虽然当前仅增加了一行代码容易撤销,但若原函数逻辑复杂且长达百行,手动逐行撤销显然不现实。因此,我们需要寻找更优雅的解决方案:


def say():
 print('Nice day')

def outer(): # 重新定义一个新函数
 print('****************') # 处理新的逻辑
 say() # 再调用原来的函数

outer()
# 现在的输出为:
'''
****************
Nice day
'''

上述方案看似满足了需求,但如果后续say()函数需要打印****************,而新加入的talk()函数也需要相同功能,难道要为每个函数单独编写一个outer()函数吗?这显然不符合DRY(Don't Repeat Yourself)原则,因此需要进一步优化:


def say():
 print('Nice day')

def talk():
 print('I am talk')

def outer(func): # 接收一个函数
 print('****************') # 处理新的逻辑
 func() # 调用传递过来的函数

outer(talk) # 把talk函数作为参数传递过去
# 输出结果如下:
'''
****************
I am talk
'''

此时,无论有多少函数需要打印****************,只需将函数名作为参数传递给outer()即可,这大大提升了便利性。但为了进一步简化调用过程,代码再次进行重构:


def say():
 print('Nice day')

def outer(func):
 def inner():
 print('****************')
 func() # 相当于 say()
 return inner

s = outer(say) # 相当于 s = inner
s() # 相当于 inner()

推测此时的输出结果应与之前一致。这里的核心逻辑是将处理过程封装在内部函数中,调用outer(say)时,将say传入outer,获取返回值inner并赋值给s。此时s等价于inner,而s()等价于inner(),因此输出结果为:

**************

Nice day

这就是最基础的装饰器实现。不过,每次使用时仍需将结果赋值给变量(如这里的s),再通过s调用,这违反了“代码精简”的原则。因此,我们引入Python特有的语法糖进行简化:


def outer(func):
 def inner():
 print('****************')
 func()
 return inner

@outer # 用outer装饰say
def say():
 print('Nice day')

say() # 调用say函数

这里将outer和say的定义顺序进行了调整,先定义outer函数。@outer语法表示使用outer来装饰say,这样直接调用say()即可实现先打印****************的功能。需要注意的是,如果不调换函数定义顺序,程序会抛出NameError: name 'outer' is not defined错误(因为作用域问题,outer尚未定义)。这算是相对复杂一点的装饰器写法。\n\n此时,读者可能会疑惑:上述示例均为无参函数,如果函数带有参数,或者参数数量不固定,该如何处理?Python结合灵活的设计完全可以解决:


# 带参数的装饰器
def outer(func):
 def inner(name):
 func(name)
 return inner

@outer
def say(name):
 print('name is %s.' % (name))

say('dandan')
# name is dandan.

上述示例中参数数量是固定的,但如果未来需求变化,需要额外传递hobby或age等参数,该如何应对?


# 带不定参数的装饰器
def outer(func):
 def inner(*args, **kwargs):
 func(*args, **kwargs)
 return inner

@outer
def say(name, age):
 print('name is %s, age is %d.' % (name, age))

@outer
def talk(name, age, hobby):
 print('name is %s, age is %d, hobby is %s.' % (name, age, hobby))

say('dandan', 18)
talk('dandan', 18, 'Coding')
'''
name is dandan, age is 18.
name is dandan, age is 18, hobby is Coding.
'''

如果新增的功能较多,单个装饰器无法覆盖,是否可以同时使用多个装饰器?答案是肯定的:


# 多个装饰器
def outer(func):
 def inner(*args, **kwargs):
 print('****************')
 func(*args, **kwargs)
 return inner

def outer2(func):
 def inner2(*args, **kwargs):
 print('这里有1w+新功能')
 func(*args, **kwargs)
 return inner2

@outer
@outer2
def say(name, age):
 print('name is %s, age is %d.' % (name, age))

@outer
@outer2
def talk(name, age, hobby):
 print('name is %s, age is %d, hobby is %s.' % (name, age, hobby))

say('dandan', 18)
talk('dandan', 18, 'Coding')
'''
****************
这里有1w+新功能
name is dandan, age is 18.
****************
这里有1w+新功能
name is dandan, age is 18, hobby is Coding.
'''

需要特别注意,多个装饰器同时作用时,执行顺序是从最外层的第一个装饰器开始,依次向内执行,最后才执行原函数本身。

至此,关于Python装饰器的深入讲解已完成。更多Python相关内容可查阅本站历史文章或继续浏览后续教程,感谢大家的支持!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表