Python中用Decorator来简化元编程的教程
少劳多得
Decorator与Python之前引入的元编程抽象有着某些共同之处:即使没有这些技术,您也一样可以实现它们所提供的功能。正如MicheleSimionato和我在可爱的Python专栏的早期文章中指出的那样,即使在Python1.5中,也可以实现Python类的创建,而不需要使用“元类”挂钩。
Decorator根本上的平庸与之非常类似。Decorator所实现的功能就是修改紧接Decorator之后定义的函数和方法。这总是可能的,但这种功能主要是由Python2.2中引入的classmethod()和staticmethod()内置函数驱动的。在旧式风格中,您可以调用classmethod(),如下所示:
清单1.典型的“旧式”classmethod
classC: deffoo(cls,y): print"classmethod",cls,y foo=classmethod(foo)
虽然classmethod()是内置函数,但并无独特之处;您也可以使用自己的方法转换函数。例如:
清单2.典型的“旧式”方法的转换
defenhanced(meth): defnew(self,y): print"Iamenhanced" returnmeth(self,y) returnnew classC: defbar(self,x): print"somemethodsays:",x bar=enhanced(bar)
decorator所做的一切就是使您避免重复使用方法名,并且将decorator放在方法定义中第一处提及其名称的地方。例如:
清单3.典型的“旧式”classmethod
classC: @classmethod deffoo(cls,y): print"classmethod",cls,y @enhanced defbar(self,x): print"somemethodsays:",x
decorator也可以用于正则函数,采用的是与类中的方法相同的方式。令人惊奇的是,这一切是如此简单(严格来说,甚至有些不必要),只需要对语法进行简单修改,所有东西就可以工作得更好,并且使得程序的论证更加轻松。通过在方法定义的函数之前列出多个decorator,即可将decorator链接在一起;良好的判断可以有助于防止将过多decorator链接在一起,不过有时候将几个decorator链接在一起是有意义的:
清单4.链接decorator
@synchronized @logging defmyfunc(arg1,arg2,...): #...dosomething #decoratorsareequivalenttoendingwith: #myfunc=synchronized(logging(myfunc)) #Nestedinthatdeclarationorder
Decorator只是一个语法糖,如果您过于急切,那么它就会使您搬起石头砸了自己的脚。decorator其实就是一个至少具有一个参数的函数——程序员要负责确保decorator的返回内容仍然是一个有意义的函数或方法,并且实现了原函数为使连接有用而做的事情。例如,下面就是decorator两个不正确的用法:
清单5.没有返回函数的错误decorator
>>>defspamdef(fn): ...print"spam,spam,spam" ... >>>@spamdef ...defuseful(a,b): ...printa**2+b**2 ... spam,spam,spam >>>useful(3,4) Traceback(mostrecentcalllast): File"<stdin>",line1,in? TypeError:'NoneType'objectisnotcallable
decorator可能会返回一个函数,但这个函数与未修饰的函数之间不存在有意义的关联:
清单6.忽略传入函数的decorator
>>>defspamrun(fn): ...defsayspam(*args): ...print"spam,spam,spam" ...returnsayspam ... >>>@spamrun ...defuseful(a,b): ...printa**2+b**2 ... >>>useful(3,4) spam,spam,spam
最后,一个表现更良好的decorator可以在某些方面增强或修改未修饰函数的操作:
清单7.修改未修饰函数行为的decorator
>>>defaddspam(fn): ...defnew(*args): ...print"spam,spam,spam" ...returnfn(*args) ...returnnew ... >>>@addspam ...defuseful(a,b): ...printa**2+b**2 ... >>>useful(3,4) spam,spam,spam 25
您可能会质疑,useful()到底有多么有用?addspam()真的是那样出色的增强吗?但这种机制至少符合您通常能在有用的decorator中看到的那种模式。
高级抽象简介
根据我的经验,元类应用最多的场合就是在类实例化之后对类中的方法进行修改。decorator目前并不允许您修改类实例化本身,但是它们可以修改依附于类的方法。这并不能让您在实例化过程中动态添加或删除方法或类属性,但是它让这些方法可以在运行时根据环境的条件来变更其行为。现在从技术上来说,decorator是在运行class语句时应用的,对于顶级类来说,它更接近于“编译时”而非“运行时”。但是安排decorator的运行时决策与创建类工厂一样简单。例如:
清单8.健壮但却深度嵌套的decorator
defarg_sayer(what): defwhat_sayer(meth): defnew(self,*args,**kws): printwhat returnmeth(self,*args,**kws) returnnew returnwhat_sayer