使用Django的时候,我发现一个很神奇的装饰器: @login_required, 这是控制一个view的权限的,比如一个视图必须登录才可以访问,可以这样用:
1
2
3
4
|
@login_required
def my_view(request):
...
return render(...)
|
同时,如果要达到这样一种效果:如果用户没有登录,那么就把用户重定向到登录界面,可以这样用:
1
2
3
4
|
@login_required (login_url = '/accounts/login/' )
def my_view(request):
...
return render(...)
|
所以这个装饰器可以带括号写,又可以不带括号写。很神奇有没有。正常的接收参数的装饰器,就算没参数也应该写成@login_required的
好奇去查了一下,在*找到一种实现,挺有意思的。先晒出答案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def doublewrap(f):
'''
a decorator decorator, allowing the decorator to be used as:
@decorator(with, arguments, and=kwargs)
or
@decorator
'''
@wraps (f)
def new_dec( * args, * * kwargs):
if len (args) = = 1 and len (kwargs) = = 0 and callable (args[ 0 ]):
# actual decorated function
return f(args[ 0 ])
else :
# decorator arguments
return lambda realf: f(realf, * args, * * kwargs)
return new_dec
|
使用起来很简单,只要给装饰器用@doublewrap装饰一下,这个装饰器就支持写括号和不写括号两种写法了。
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
|
def test_doublewrap():
from util import doublewrap
from functools import wraps
@doublewrap
def mult(f, factor = 2 ):
'''multiply a function's return value'''
@wraps (f)
def wrap( * args, * * kwargs):
return factor * f( * args, * * kwargs)
return wrap
# try normal
@mult
def f(x, y):
return x + y
# try args
@mult ( 3 )
def f2(x, y):
return x * y
# try kwargs
@mult (factor = 5 )
def f3(x, y):
return x - y
assert f( 2 , 3 ) = = 10
assert f2( 2 , 5 ) = = 30
assert f3( 8 , 1 ) = = 5 * 7
|
原理也不难,只有短短不到10行代码。
装饰器我们都知道,是用来处理一个函数,返回一个新的函数的(如果你不理解装饰器,可以看一下这个经典的解释)。
1
|
new_func = decorator(func)
|
我们使用的,就是被装饰器装饰的新函数了。装饰器只是一个语法糖,其实它也是一个函数,给它传入一个函数作为参数,就返回一个新的函数。那么既然装饰器也是一个函数,我们就可以用装饰器装饰这个函数。也就是,“装饰器的装饰器”。
装饰器第一个参数肯定是原函数,如果装饰器可以接收参数的话,要么第一个参数是原函数,后面跟别的参数;要么就只有原函数一个参数。所以,我们这个“装饰器的装饰器”做的事情就是,判断装饰器接收的参数,如果只有一个并且第一个参数是可调用的(callable),那么这就是一个无参数的装饰器(不需要加括号)。如果还有别的参数,就返回一个生成装饰器的函数(decorator_maker)。
装饰器是一个函数。装饰器被装饰过之后,这个装饰器运行之前就会先运行装饰器的装饰器的代码,也就是我们的doublewrapp。然后返回值可能是一个装饰器,也可能是一个装饰器的maker(有参数的装饰器),然后装饰器再执行,装饰原函数。
这里有点绕,因为本来装饰器里面一般就会有三四层函数了,(maker, decorator, wrapper, realfunc),再加上一个装饰器的装饰器,会有点理解困难。如果理解不了,最好不要对着网上的博文(包括本文)企图格物致知了,多去看看代码,多写一写。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
参考资料
How to create a Python decorator that can be used either with or without parameters?
原文链接:https://www.kawabangga.com/posts/1969