decorator 本质就是一个接收 对象 的 对象 (对,是个对象,而不是大多数人认为的函数),更多的资料可以参照 理解python的装饰器 | Darkof
final_func = decorator(wrapped_function) # 与注释部分的实质是一致的。 @decorator defwrapped_func(*args, **kwargs): pass 被装饰的函数与之前相比,改变了什么? 行为这个是最显而易见的,装饰器可以在原函数执行之前或之后添加额外的行为
函数本身的属性如果你简单的实现了下面的 decorator 会改变什么呢
deffoo(func): defwrapped(*args, **kwargs): print "in decorator" return func(*args, **kwargs) return wrapped classbar(object): # 原谅我用小写的 bar :) def__init__(self, func): self.func= func def__call__(self, *args, **kwargs): print "in decorator" return self.func(*args, **kwargs) @foo deff1(a): print a print f1.__name__ #输出: 'wrapped' @bar deff2(a): print a print f2.__name__ # AttributeError Traceback (most recent call last) # <ipython-input-9-dff5600c49e8> in <module>() # ----> 1 f2.__name__ # # AttributeError: 'bar' object has no attribute '__name__'从上面我们看出来,函数的 __name__ 属性也发生了变化,这也是为什么我们推荐装饰器的时候使用 fucntools.wraps
import functools deffoo(func): @functools.wraps(func) defwrapped(*args, **kwargs): print "in decorator" return func(*args, **kwargs) return wrappedwraps 会把原函数的属性赋给新的 wrapped 这个函数(主要会同步的属性为 __name__ , __module__ , __doc__ , __dict__ , 当然,你也可以添加你希望同步的属性)
函数的参数是的,很少有人会注意到被装饰过会,函数接收的参数也会产生变化
inspect.getargspec(f1) # 输出 ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None) inspect.getargspec(f2) # TypeError Traceback (most recent call last) # <ipython-input-16-bff760b02fba> in <module>() # ----> 1 inspect.getargspec(f2) # /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.pyc in getargspec(func) # 814 func = func.im_func # 815 if not isfunction(func): # --> 816 raise TypeError('{!r} is not a Python function'.format(func)) # 817 args, varargs, varkw = getargs(func.func_code) # 818 return ArgSpec(args, varargs, varkw, func.func_defaults) # TypeError: <__main__.bar object at 0x108729f50> is not a Python functionf1 接收的函数名从 a 变成了 args 和 kwargs, f2 干脆就拿不到了,这也意味着其实装饰器并不能做到 works anywhere(毕竟有很多装饰器会通过参数来判断这是不是一个 classmethod,然后两个装饰器混用可能会导致其中一个失效)
装饰 classmethod/staticmethod import functools deffoo(func): defwrapped(*args, **kwargs): print "in decorator" return func(*args, **kwargs) return wrapped classBar(object): @foo @classmethod defduck(cls): print 'Yooooooooooo!' Bar.duck() # --------------------------------------------------------------------------- # TypeError Traceback (most recent call last) # <ipython-input-19-ddf54c241cc4> in <module>() # ----> 1 Bar.duck() # # TypeError: unbound method wrapped() must be called with Bar instance as first argument (got nothing instead)当我们尝试用之前实现的 decorator 来装饰 classmethod 的时候,会遇到 TypeError,原因是 classmethod/staticmethod 本质是一个 Descriptor 而非 function,我们在一开始提到这样一句话:
decorator 本质就是一个接收 对象 的 对象
在前面也聊到了,decorator 是个对象,可能是个 class 或是 function, 那么他接收的是什么呢,很多人认为 decorator 接受的是函数,然而严格来说,decorator 接收的是一个 对象
当我们知道 classmethod/staticmehtod 是 Descriptor 之后就很容易的知道如何写一个装饰 Descriptor(当然,你需要先了解什么是 Descriptor )
classbar(object): # 原谅我用小写的 bar :) def__init__(self, func): self.func= func def__call__(self, *args, **kwargs): print "in decorator" return self.func(*args, **kwargs) classfoo(object): def__init__(self, func): self.func = func def__get__(self, instance, owner): func = self.func.__get__(instance, owner) return bar(func) def__call__(self, *args, **kwargs): return bar(self.func)(*args, **kwargs) classDuck(object): @foo @staticmethod deffly(): print 'fly' @foo @classmethod defrun(cls): print 'run' @foo defstop(self): print 'stop' Duck.fly() # 输出 # in decorator # fly Duck.run() # 输出 # in decorator # run Duck().stop() # 输出 # in decorator # stop