需要理解的一些概念
要理解Python中的装饰器,我觉得还是应该从最基本的概念开始:
装饰器模式:所谓的装饰器模式,可以简单地理解为“在不改变原有内部实现的情况下,为函数或者类添加某种特性”。这样我们就可以将一些与业务无关、具有通用性的代码抽象出来,作为装饰器附加到需要这些代码的函数或者类之上。用面向切面编程的思想解释就是“装饰器应该是一个切面”。
函数是一等公民:意思就是函数可以被当成普通变量一样使用。在Python中,可以把函数赋值给变量,可以将函数作为其它函数的参数,也可以将函数作为其它函数的返回值。
闭包:我们都知道局部作用域可以引用全局作用域中的变量,相似的,当一个函数内部又定义了其它函数的时候,内部函数可以使用外部函数所在作用域的变量,这就是闭包。
从最简单的装饰器做起
理解完以上的概念之后,我们尝试一下利用这些特性实现一个简单的装饰器。
首先明确一下需求,我们有时候会需要在函数调用时打印一个相应的日志,虽然可以通过在所有需要打印日志的函数代码中嵌入打印日志的代码来实现,但这种方法不仅增加了许多重复代码,而且在业务代码中嵌入与业务无关的代码增加了整体的耦合度。因此,我们需要实现一个装饰器,这个装饰器在函数调用时可以打印一个日志记录函数调用行为。
如果我们有以下函数foo,代表具体的业务函数:
def foo(): print('in function foo')
我们设想通过调用foo = deco(foo)实现给函数foo增加打印日志的功能,并且不影响它原有的业务。那么在这种设想下,装饰器deco应该也是一个函数,它接收另一个函数作为输入,并返回一个新的、经过装饰的函数。在Python中,我们可以这么写:
def deco(func): # 接收一个函数作为参数 def new_func(): print(f'[log] run function {func.__name__}') # 此处使用了Python3.6的格式化字符串 func() # 闭包,在内部函数中使用了外部函数的变量 return new_func # 将新函数作为返回值返回
执行一下试试效果:
>>> foo = deco(foo)>>> foo()[log] run function fooin function foo
不错,至此我们已经实现了一个最简单的装饰器!在上面的代码中,装饰器deco接收任意的函数作为参数,再在内部构造另一个函数,利用闭包的特性,可以在新函数里调用存在于装饰器deco局部作用域中的函数func。
神奇的@
按照上面那么写,每次我们都得为需要装饰的函数赋一个新值,万一函数或者装饰器的数量增加了,手动写赋值和函数调用就会变得非常麻烦。那么在Python中,有没有更优雅的写法呢?答案是有的,你只需要一个@符号。
新闻热点
疑难解答