Python装饰器原理深度解析:从函数对象到语法糖
本文深入解析Python装饰器原理,通过风扇类比解释函数作为对象传递的概念。详细拆解装饰器实现过程,从手动调用wrapper到使用@语法糖,阐明如何在不修改原代码前提下添加前置或后置操作。涵盖带参方法处理,帮助开发者彻底理解装饰器执行逻辑。
Python装饰器的作用
作用
装饰器的核心功能在于对函数或方法进行增强。它允许开发者在不修改原有函数或方法代码的前提下,为其添加前置处理或后置处理逻辑。
例如,当需要统计各个函数的执行时间时,若直接在每个函数内部添加time.time()调用,会导致代码冗余且难以维护。
用法
网络上关于装饰器写法的教程众多,但建议先深入理解其底层原理,再掌握具体写法,这样能更灵活地应对各种场景。
装饰器实现原理
实现
理解装饰器的实现机制远比单纯记忆写法重要。简而言之,装饰器是一个接收函数对象作为参数的过程,它先执行前置操作,接着调用原函数,最后执行后置操作。
这种抽象描述可能难以直观理解,我们可以借助一个贴近生活的例子来类比。
假设你拥有这样一台小风扇:
这台风扇支持充电,配备开关。打开开关后扇叶旋转开始工作;你可以插电使用,也可以充满电后携带至他处使用。
在“一切皆对象”的Python中,这台风扇即可视为一个对象,其核心功能是产生风力。
def fengshan():
return '出风'
为了便于与示例结合,我们采用拼音命名方式。
此时,我们定义了一个名为fengshan的函数,其返回值是字符串“出风”。
具备一定Python基础的用户应当清楚如何调用此方法。
def fengshan():
return '出风'
print(fengshan())
请勿觉得此基础概念繁琐,理解装饰器必须掌握这一基石:调用函数的方式是函数名后加括号fengshan()。这里的括号()等同于执行函数的开关,若不加括号:
def fengshan():
return '出风'
print(fengshan)
返回的将是函数对象本身(即示例中的风扇实体)。
<function fengshan at 0x7f8e7c4a6950>
这表示名为fengshan的函数对象,其内存地址为0x7f8e7c4a6950。
此时,我们可以将该函数对象“带走”,并在其他位置使用。
def fengshan():
return '出风'
func = fengshan
print(fengshan)
print(func)
返回
<function fengshan at 0x7f570eaf3950> <function fengshan at 0x7f570eaf3950>
这表明变量func与fengshan指向同一内存地址,二者等价。因此,执行func()等同于执行fengshan()。
def fengshan():
return '出风'
func = fengshan
print('下面是执行fengshan')
print(fengshan())
print('下面是执行func')
print(func())
返回
以下是执行fengshan()的结果:
出风
以下是执行func()的结果:
出风
理解至此,即可明白装饰器的实现逻辑。让我们通过另一个示例进一步说明。
def fengshan():
return '出风'
def wrapper(func):
return func
print(fengshan)
print(wrapper(fengshan))
此例中,除了保留原有的fengshan函数外,还定义了一个wrapper函数。
由于Python中一切皆对象,函数也是对象,且函数的参数可以接收对象,因此函数对象可作为参数传递给另一个函数wrapper。
该wrapper函数未做额外处理,仅返回接收到的func对象。打印两个对象可知,它们实际上是同一对象。
<function fengshan at 0x7f9b0c92f950> <function fengshan at 0x7f9b0c92f950>
至此,你已理解装饰器的基本实现。若跟随代码敲写一遍,便已编写了一个简易装饰器。只需稍加修改,例如在wrapper内部执行接收的func,并在其前后添加操作:
def wrapper(func):
print('在wrapper中执行func前')
print(func())
print('在wrapper中执行func后')
wrapper(fengshan)
返回
在wrapper中执行func前
出风
在wrapper中执行func后
若觉得神奇难懂,可回顾风扇图片重读。理解上述代码后,我们将加速完成真正的装饰器编写。
装饰器的实际用法与语法糖
用法
当理解函数定义与调用过程后,我们考虑实际场景。上一段代码中,我们改变了原有函数的调用方式。
调用方式从fengshan()变为func(fengshan)。
为实现不改变原有代码和调用方式即可添加前置或后置操作,需进一步优化装饰器。
def fengshan():
print('出风')
def wrapper(func):
def f():
print('在wrapper中执行func前')
func()
print('在wrapper中执行func后')
return f
fengshan = wrapper(fengshan)
fengshan()
为清晰展示执行过程,将fengshan内部的return改为print。
返回
在wrapper中执行func前
出风
在wrapper中执行func后
此处的改动是在wrapper原有实现中再包裹一层方法f。回顾风扇例子,执行wrapper()时发生了什么?
wrapper(func)返回的是一个函数对象f,该对象的开关未打开,f内部代码不会立即执行。
我们再用同名变量fengshan接收此函数对象f,并在最后一行打开fengshan的开关——fengshan()。此时,函数对象f中的代码才被执行。
若仍不理解,建议从风扇图片开始重读。若已理解,建议再复习一遍。至此,我们已完成一个装饰器的编写。为便于使用,Python提供了更简便的方法。
def wrapper(func):
def f():
print('在wrapper中执行func前')
func()
print('在wrapper中执行func后')
return f
@wrapper
def fengshan():
print('出风')
fengshan()
返回
在wrapper中执行func前
出风
在wrapper中执行func后
支持带参方法的装饰器
补充知识
若已理解装饰器的执行逻辑,便知如何让装饰器支持带参数的方法,这也是编写装饰器的常规操作。
def wrapper(func):
def f(*args, **kwargs):
print('在wrapper中执行func前')
func(*args, **kwargs)
print('在wrapper中执行func后')
return f
@wrapper
def fengshan(str_obj):
print(str_obj)
fengshan(str_obj='出风')
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









