深入学习Python中的装饰器使用
装饰器 vs 装饰器模式 宏的历史 宏的目标 能用Python装饰器做些什么 @entryExit def func1(): print "inside func1()" @entryExit def func2(): print "inside func2()" 函数的装饰器 @myDecorator def aFunction(): print "inside aFunction" 当编译器走到这段代码的时候,函数aFunction会被编译,编译得到的函数对象会传递给myDecorator,装饰器会生成一个新的函数对象来替换原有的函数aFunction。 class myDecorator(object): def __init__(self,f): print "inside myDecorator.__init__()" f() # Prove that function definition has completed def __call__(self): print "inside myDecorator.__call__()" @myDecorator def aFunction(): print "inside aFunction()" print "Finished decorating aFunction()" aFunction() 当你执行这段代码的时候,你会看到这样的输出: inside myDecorator.__init__() inside aFunction() Finished decorating aFunction() inside myDecorator.__call__() 请注意,myDecorator的构造器实际是在装饰函数的时候执行的。我们可以在__init__()里面调用函数f,能够看到,在装饰器被调用之前,函数调用f()就已经完成了。另外,装饰器的构造器能够接收被装饰的方法。一般来讲,我们会捕捉到这个函数对象然后接下来在函数__call__()里面调用。装饰和调用是两个非常清晰明了的不同的步骤,这也是我为什么说类似装饰器更简单同时也更强大的原因。 def foo(): pass foo = staticmethod(foo) 因为有了@这个装饰器操作符, 你可以非常优雅的得到同样的结果: @staticmethod def foo(): pass 不过也有不少人因为这一点反对装饰器,不过@仅仅是一个很小的语法糖而已,把一个函数对象传递给另外一个函数,然后用返回值替换原有的方法。 青出于蓝 class entryExit(object): def __init__(self,f): self.f = f def __call__(self): print "Entering",self.f.__name__ self.f() print "Exited",self.f.__name__ @entryExit def func1(): print "inside func1()" @entryExit def func2(): print "inside func2()" func1() func2() 运行结果是: Entering func1 inside func1() Exited func1 Entering func2 inside func2() Exited func2 现在我们能够看到,那些被装饰的方法有了“进入”和“离开”的跟踪信息。 使用函数作为装饰器 def entryExit(f): def new_f(): print "Entering",f.__name__ f() print "Exited",f.__name__ return new_f @entryExit def func1(): print "inside func1()" @entryExit def func2(): print "inside func2()" func1() func2() print func1.__name__
def entryExit(f): def new_f(): print "Entering",f.__name__ new_f.__name__ = f.__name__ return new_f 你可以动态的获取函数的信息包括那些你做的更改,这在python里面非常有用。 带参数的装饰器 class decoratorWithArguments(object): def __init__(self,arg1,arg2,arg3): """ If there are decorator arguments,the function to be decorated is not passed to the constructor! """ print "Inside __init__()" self.arg1 = arg1 self.arg2 = arg2 self.arg3 = arg3 def __call__(self,f): """ If there are decorator arguments,__call__() is only called once,as part of the decoration process! You can only give it a single argument,which is the function object. """ print "Inside __call__()" def wrapped_f(*args): print "Inside wrapped_f()" print "Decorator arguments:",self.arg1,self.arg2,self.arg3 f(*args) print "After f(*args)" return wrapped_f @decoratorWithArguments("hello","world",42) def sayHello(a1,a2,a3,a4): print 'sayHello arguments:',a1,a4 print "After decoration" print "Preparing to call sayHello()" sayHello("say","hello","argument","list") print "after first sayHello() call" sayHello("a","different","set of","arguments") print "after second sayHello() call" 从输出结果来看,运行的效果发生了明显的变化: Inside __init__() Inside __call__() After decoration Preparing to call sayHello() Inside wrapped_f() Decorator arguments: hello world 42 sayHello arguments: say hello argument list After f(*args) after first sayHello() call Inside wrapped_f() Decorator arguments: hello world 42 sayHello arguments: a different set of arguments After f(*args) after second sayHello() call 现在,在“装饰”阶段,构造器和__call__()都会被依次调用,__call__()也只接受一个函数对象类型的参数,而且必须返回一个装饰方法去替换原有的方法,__call__()只会在“装饰”阶段被调用一次,接着返回的装饰方法会被实际用在调用过程中。 带参数的函数式装饰器 def decoratorFunctionWithArguments(arg1,arg3): def wrap(f): print "Inside wrap()" def wrapped_f(*args): print "Inside wrapped_f()" print "Decorator arguments:",arg3 f(*args) print "After f(*args)" return wrapped_f return wrap @decoratorFunctionWithArguments("hello","arguments") print "after second sayHello() call" 输出结果: Inside wrap() After decoration Preparing to call sayHello() Inside wrapped_f() Decorator arguments: hello world 42 sayHello arguments: say hello argument list After f(*args) after first sayHello() call Inside wrapped_f() Decorator arguments: hello world 42 sayHello arguments: a different set of arguments After f(*args) after second sayHello() call 函数式装饰器的返回值必须是一个函数,能包装原有被包装函数。也就是说,Python会在装饰发生的时候拿到并且调用这个返回的函数结果,然后传递给被装饰的函数,这就是为什么我们在装饰器的实现里嵌套定义了三层的函数,最里层的那个函数是新的替换函数。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |