Python装饰器原理与简单用法实例分析
本文深入解析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程序设计学习有所帮助。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









