函数和方法装饰漫谈(Function decorator)
装饰方法的产生:
Python2.2通过增加静态方法和类方法扩展了Python的对象模型。但是当时没有提供一个简化的语法去定义static/class方法,只得在定义好的方法尾部去调用staticmethod()/classmethod()方法达到目的。例如:
def meth (cls):
meth = classmethod(meth) # 使meth方法成为类方法
但是这样会造成一个问题:当一个方法比较长时,很容易忘记尾部的调用。为了简化这个操作一个新的语法被加了进来:方法装饰,以@开头后跟装饰方法名,如@staticmethod/@classmethod,由此产生出decorator方法及decorator模式。现在我们可以这样写:
@classmethod
def meth (cls):
可以对一个方法应用多个装饰方法:
@B
@C
def f ():
#等价于下面的形式,Python会按照应用次序依次调用装饰方法(最近的先调用)
def f():
f = A(B(C(f)))
装饰方法解析:
每个decorator只是一个方法, 可以是自定义的或者内置的(如内置的@staticmethod/@classmethod)。decorator方法把要装饰的方法作为输入参数,在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大,会有很多应用场景),只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数, 或者是一个新函数)。decorator的作用对象可以是模块级的方法或者类方法。decorator根据应用时的参数个数不同分为两类:无参数decorator,有参数decorator。下面分别介绍。
无参数decorator:
"""无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法"""
print "Enter decorator" #进行额外操作
func.attr = 'decorated' #对函数进行操作,增加一个函数属性
return func #返回一个可调用对象(此例还是返回作为输入参数的方法)
#返回一个新函数时,新函数可以是一个全局方法或者decorator函数的内嵌函数,
#只要函数的签名和被装饰的函数相同
@deco
def MyFunc(): #应用@deco修饰的方法
print "Enter MyFunc"
MyFunc() #调用被装饰的函数
注意:当使用上述方法定义一个decorator方法时,函数体内的额外操作只在被装饰的函数首次调用时执行,如果要保证额外操作在每次调用被装饰的函数时都执行,需要换成如下的写法:
def replaceFunc(): #定义一个内嵌函数,此函数包装了被装饰的函数,并提供额外操作的代码
print "Enter decorator" #进行额外操作
return func() #产生对被装饰函数的调用
return replaceFunc #由于返回的是这个新的内嵌函数,所以确保额外操作每次调用得以运行
@deco
def MyFunc(): #应用@deco修饰的方法
print "Enter MyFunc"
MyFunc() #调用被装饰的函数
有参数decorator:
"""由于有参数的decorator函数在调用时只会使用应用时的参数而不接收被装饰的函数做为参数,
所以必须返回一个decorator函数, 由它对被装饰的函数进行封装处理"""
def newDeco(func): #定义一个新的decorator函数
def replaceFunc(): #在decorator函数里面再定义一个内嵌函数,由它封装具体的操作
print "Enter decorator" #进行额外操作
return func() #对被装饰函数进行调用
return replaceFunc
return newDeco #返回一个新的decorator函数
@decoWithArgs("demo")
def MyFunc(): #应用@decoWithArgs修饰的方法
print "Enter MyFunc"
MyFunc() #调用被装饰的函数
当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现。