Decorator 的本质是什么?
decorator 本质就是一个接收对象的对象(对,是个对象,而不是大多数人认为的函数),更多的资料可以参照 理解Python的装饰器 | Darkof1
2
3
4
5final_func = decorator(wrapped_function) # 与注释部分的实质是一致的。
def wrapped_func(*args, **kwargs):
pass
被装饰的函数与之前相比,改变了什么?
行为
这个是最显而易见的,装饰器可以在原函数执行之前或之后添加额外的行为
函数本身的属性
如果你简单的实现了下面的 decorator 会改变什么呢1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32def foo(func):
def wrapped(*args, **kwargs):
print "in decorator"
return func(*args, **kwargs)
return wrapped
class bar(object): # 原谅我用小写的 bar :)
def __init__(self, func):
self.func= func
def __call__(self, *args, **kwargs):
print "in decorator"
return self.func(*args, **kwargs)
def f1(a):
print a
print f1.__name__
#输出: 'wrapped'
def f2(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
1 | import functools |
wraps
会把原函数的属性赋给新的 wrapped 这个函数(主要会同步的属性为 __name__
, __module__
, __doc__
, __dict__
, 当然,你也可以添加你希望同步的属性)
函数的参数
是的,很少有人会注意到被装饰过会,函数接收的参数也会产生变化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16inspect.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 function
f1 接收的函数名从 a 变成了 args 和 kwargs, f2 干脆就拿不到了,这也意味着其实装饰器并不能做到 works anywhere(毕竟有很多装饰器会通过参数来判断这是不是一个 classmethod,然后两个装饰器混用可能会导致其中一个失效)
装饰 classmethod/staticmethod
1 | import functools |
当我们尝试用之前实现的 decorator 来装饰 classmethod 的时候,会遇到 TypeError,原因是 classmethod/staticmethod 本质是一个 Descriptor 而非 function,我们在一开始提到这样一句话:
decorator 本质就是一个接收对象的对象
在前面也聊到了,decorator 是个对象,可能是个 class 或是 function, 那么他接收的是什么呢,很多人认为 decorator 接受的是函数,然而严格来说,decorator 接收的是一个对象
当我们知道 classmethod/staticmehtod 是 Descriptor 之后就很容易的知道如何写一个装饰 Descriptor(当然,你需要先了解什么是 Descriptor)
1 | class bar(object): # 原谅我用小写的 bar :) |