装饰器的名字很有意思,在某个函数需要使用装饰器的时候头上挂一个@xyz,这个函数就有了头上这个装饰器里预先实现的特殊功能。
想要明白装饰器的实现原理,首先要知道两个概念,函数是Python里的一等公民和装饰器是可以生成函数的函数。
01. 何为一等公民#
一等公民
意味着函数可以像其他任何数据类型(如整数、字符串、列表等)一样被处理。
具体来说,函数可以被赋值给变量、作为参数传递给其他函数、作为函数的返回值,甚至可以存储在数据结构中。
这种特性使得 Python 的函数非常灵活和强大。
装饰器的实现就是函数可以作为参数传递给其他函数、作为函数的返回值
这个的完整阐述。
02. 一切皆对象#
一切皆对象
意味着 Python 中的每一个事物,包括变量、函数、类、模块等,都是对象。
函数当然也不例外,函数内部也有数据和方法,可以在运行时动态修改,改变函数的行为。
函数内部也有数据和方法,看起来可能比较奇怪,通常类实例化后的对象才有数据和方法,函数里为什么也会有数据和方法(函数)呢?
这个奥秘就在一切皆对象
里。
Python里的普通变量,比如字符串,整数,字典,或元组,字面意义上代表某个内存里的数据,是一种数据对象。 class定义一个类,实例化后的东西,也叫对象,这个对象不仅仅是数据的容器,更重要的是它具有行为,可以理解成一种以行为为主的对象。 def定义的函数,表面看主要供调用者Call使用,传入一个东西,返回另一个东西,但在Python内部的表示形式并没那么简单。
首先看一个数据对象比如列表,在Python内部都有什么东东
默认可以点(·)
出来的是Python内置到列表对象里常用的方法,用来对当前列表对象做一些操作。
继续在点后边输入下划线,tab提示出来了更多的东西,在Python里叫魔术方法和魔术属性,了解这些可以开发出一些更高级的功能,比如装饰器等等。
函数的通常用法是 func_name(arg1, arg2)
,但函数同样也有上边那些以下划线开头的魔术属性和方法。
换句话说,如果一个对象里边的这些属性和方法和某个函数里的这些内容相同,这个对象基本上等同于这个函数。
理解了这些,就能明白,函数的产生不仅限于def或lambda,还可以通过代码的形式动态生成,甚至类也一样,属于元编程更高级的话题了。
函数可以通过代码生成,同时函数可以作为参数传给另一个函数,那么如果把某个函数A传入一个特定函数D,然后在函数D里通过代码把函数A的某些属性取到或修改就成为可能了。
03. 实例#
装饰器最常用的例子就是打日志或计算函数运行耗时。
下边是个完整实例,装饰器log_decorator
记录了函数调用时的参数值和函数执行开始到退出的耗时。
log_decorator
函数接受一个函数func作为参数,定义了一个wrapper
函数,然后把这个wrapper
函数返回。
wrapper
函数先是打印了func的调用记录,然后去实际调用传入的函数func,这一步保证了被装饰的函数的原功能正常运行。
然后在调用原函数前后计算执行耗时。
执行结果如下:
INFO:__main__:get_order called with args: ('KN001',), kwargs: {'status': 'CREATED'}Entry get_order func KN001 CREATEDExit get_order func KN001 CREATEDINFO:__main__:get_order finished in 0.41 seconds
从输出结果可以看出,调用被装饰的函数,其实就是在执行装饰器中定义的wrapper函数。 示例中装饰器做的事情比较简单,还可以进行异常捕获统一处理。 fastapi等类似框架更是大量使用这一技术,同样的功能用了装饰器可以让开发者写更少的代码。
装饰器只是一个语法糖,本质上是把def定义好的一个函数传给装饰器函数,生成一个新的函数。
20行的@log_decorator
可以在get_order
定义后用下边的代码替代:
get_order = log_decorator(get_order)
如此而已。
04. Tip#
注意第10行的@functools.wraps(func)
,新函数wrapper
在返回前又被装饰了一次。
如果去掉这一行,运行结果好像一样,但在某些场景下会有奇怪的表现。
如果没有用@functools.wraps(func)
装饰一次wrapper
,新生成的函数对象里内置的魔术属性和方法是装饰器中wrapper
定义时的内容。
比如上图中,如果在代码其他地方需要用到这些属性,会发现被装饰后的函数丢失了自己的名字和文档字符串,会带来一些预料不到的bug。
@functools.wraps(func)
的作用就是把原函数的马甲套到生成的新函数上,保证对象除了基本行为之外的一致性。