直到现在我也只是能明白比较简单的 Decorator,可能是我的脑回路比较短吧.
最近我感到费解的便是在看 Django 的 `permission_required` 这个 `decorator` 的代码时觉得很难下手.
我去看 Django 的源代码是我一个朋友想在 ModelAdmin 中的 Action 中使用 `permission_required`,结果不行
因为 `permission_required` 只能用在 view 函数中.
## `@` Decorator 语法
Decorator 从几年前我接触时,对我烧脑感觉就是,我无法知道他是做什么的,
只会文档让我用什么,我就用什么,比如最开始的 `@property`,`@classmethod`
很长一段时间我除了使用系统内置的这几个 Decorator 之外就没有尝试过其他的,更别谈自己写 Decorator 了.
老实来说,我在学校系统学习的是 Java,而 Java 也有这种 `@InjectView` 这种以 `@` 开头的用法,一开始
我先入为主的以为 Python 中的 Decorator 也类似于 Java 中的 注解,为此我还有些高兴,因为 Java 中的注解我是比较熟悉的.
这是我错误的第一步,后来我通过学习才感觉事实完全不是我想的那样的, Decorator 根本不是注解.而是一种函数嵌套调用的方便写法,
```py
@decor1
def func1:pass
```
相当于 `deocor1(func1)`
## 烧脑的 wrap
有一次我发现很多的 `Decorator` 都有使用一个 `functools.wraps` 函数.
```py
def wraps(wrapped,assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped, assigned=assigned,updated=updated)
```
然后我首先我费力的了解了 `partial` 其实是在通过设置预置的参数来特殊某一个函数以生成新的函数 .
很多时候我都会忘记`wraps` 是用来做什么的? 经常查询文档我发现,它其实是可有可无的东西,有更好,没有,一般情况下也不影响功能.
`wraps` 主体在于 `update_wrapper` 这样的函数,其实很多时候也是比较费脑了,包装者与被包装者,我是这样来理解
`wrapper`,`wrapped`. 这让我想起"爱情公寓" 中陈美佳分不清,委托人和被委托人一样.
文档中对这个方法的说明是:"更新一个包装函数使它看起来像被包装的函数"
不过看到两人默认的参数值的时候,我仿佛明白了点什么:
```py
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
```
将被包装的函数的 `__module__`,`__name__`,`__doc__` 属性复制到 包装函数中去,
将被包装的函数 的`__dict__` 中的数据合并到 包装函数中去.
而要真正的理解这里为什么需要这样做,是需要明白下面的代码到底做了什么:
```
@decorator
def func1():pass
```
前面我已经理解了它是 `decorator(func1)`
但是还有另一个隐含的结果我没有深入理解.
比如在下面的代码:
```py
// foo.py
def func1():pass
```
需要理解成,foo 模型有一个名为 `func1` 的标识符绑定到了`func1` 声明的函数体中.
而
```py
@decorator
def func1():pass
```
需要理解成下面的代码
```py
def func1():pass
func1 = decorator(func1)
```
很关键的一点是,`func1` 现在被重新赋值了,`func1` 这个标识符被绑定到了 `decorator(func1)` 的执行返回结果去了.
## 这么多的嵌套
当我在看 Django 的 `permission_required` 函数时,我大概已经掌握了上面我说的烧脑的点,
但是还是抵不住 `permission_required` 函数有众多层的嵌套啊.
```py
def permission_required(perm, login_url=None, raise_exception=False):
def check_perms(user):pass
return user_passes_test(check_perms,login_url=login_url)
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
def decorator(view_func):
@wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
return _wrapped_view
return decorator
```
函数套函数,函数传函数, 啊,让我静一静先.
首先我需要明白:
```py
@permission_required('polls.can_vote')
def my_index(request):pass
```
这样的代码,my_index 最后是绑定到哪一个函数去了?
首先是推算出 `permission_required('polls.can_vote')` 的调用结果
也就是: `user_pass_test` 的调用结果.
它的调用结果是返回 其内部定义的名为: `decorator` 函数.
也就是说 `permission_required('polls.can_vote')` 的调用结果为:
`deorator = permission_required('polls.can_vote')`
然后也就是说:
`my_index = decorator(my_index)`
而 `decorator` 的调用结果是 名为 `_wrapped_view` 这个函数
也就是说最后:
`my_index = _wrapped_view`
调用 `my_index` 就相当于调用 `__wrapped_view`
而原来的 `my_index` 所指向的函数体,就是由其中的`view_func` 闭包函数变量来指向.
到这里,我基本学会了,面对复杂的 `Decorator` 函数就是层层剥开它来理解他.