python if后面要不要加括号_Python装饰器兼容加括号与不加括号的写法
使用Django的時候,我發現一個很神奇的裝飾器:@login_required, 這是控制一個view的權限的,比如一個視圖必須登錄才可以訪問,可以這樣用:
1
2
3
4
@login_required
defmy_view(request):
...
returnrender(...)
同時,如果要達到這樣一種效果:如果用戶沒有登錄,那么就把用戶重定向到登錄界面,可以這樣用:
1
2
3
4
@login_required(login_url='/accounts/login/')
defmy_view(request):
...
returnrender(...)
所以這個裝飾器可以帶括號寫,又可以不帶括號寫。很神奇有沒有。正常的接收參數的裝飾器,就算沒參數也應該寫成@login_required的
好奇去查了一下,在stackoverflow找到一種實現,挺有意思的。先曬出答案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defdoublewrap(f):
'''
a decorator decorator, allowing the decorator to be used as:
@decorator(with, arguments, and=kwargs)
or
@decorator
'''
@wraps(f)
defnew_dec(*args,**kwargs):
iflen(args)==1andlen(kwargs)==0andcallable(args[0]):
# actual decorated function
returnf(args[0])
else:
# decorator arguments
returnlambdarealf:f(realf,*args,**kwargs)
returnnew_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
deftest_doublewrap():
fromutilimportdoublewrap
fromfunctoolsimportwraps
@doublewrap
defmult(f,factor=2):
'''multiply a function's return value'''
@wraps(f)
defwrap(*args,**kwargs):
returnfactor*f(*args,**kwargs)
returnwrap
# try normal
@mult
deff(x,y):
returnx+y
# try args
@mult(3)
deff2(x,y):
returnx*y
# try kwargs
@mult(factor=5)
deff3(x,y):
returnx-y
assertf(2,3)==10
assertf2(2,5)==30
assertf3(8,1)==5*7
原理也不難,只有短短不到10行代碼。
裝飾器我們都知道,是用來處理一個函數,返回一個新的函數的(如果你不理解裝飾器,可以看一下這個經典的解釋)。
1
new_func=decorator(func)
我們使用的,就是被裝飾器裝飾的新函數了。裝飾器只是一個語法糖,其實它也是一個函數,給它傳入一個函數作為參數,就返回一個新的函數。那么既然裝飾器也是一個函數,我們就可以用裝飾器裝飾這個函數。也就是,“裝飾器的裝飾器”。
裝飾器第一個參數肯定是原函數,如果裝飾器可以接收參數的話,要么第一個參數是原函數,后面跟別的參數;要么就只有原函數一個參數。所以,我們這個“裝飾器的裝飾器”做的事情就是,判斷裝飾器接收的參數,如果只有一個并且第一個參數是可調用的(callable),那么這就是一個無參數的裝飾器(不需要加括號)。如果還有別的參數,就返回一個生成裝飾器的函數(decorator_maker)。
裝飾器是一個函數。裝飾器被裝飾過之后,這個裝飾器運行之前就會先運行裝飾器的裝飾器的代碼,也就是我們的doublewrapp。然后返回值可能是一個裝飾器,也可能是一個裝飾器的maker(有參數的裝飾器),然后裝飾器再執行,裝飾原函數。
這里有點繞,因為本來裝飾器里面一般就會有三四層函數了,(maker, decorator, wrapper, realfunc),再加上一個裝飾器的裝飾器,會有點理解困難。如果理解不了,最好不要對著網上的博文(包括本文)企圖格物致知了,多去看看代碼,多寫一寫。
2017年12月31日更新:《Python3 Cookbook》中提供了另一種實現方法,代碼如下:
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
fromfunctoolsimportwraps,partial
importlogging
deflogged(func=None,*,level=logging.DEBUG,name=None,message=None):
iffuncisNone:
returnpartial(logged,level=level,name=name,message=message)
logname=nameifnameelsefunc.__module__
log=logging.getLogger(logname)
logmsg=messageifmessageelsefunc.__name__
@wraps(func)
defwrapper(*args,**kwargs):
log.log(level,logmsg)
returnfunc(*args,**kwargs)
returnwrapper
# Example use
@logged
defadd(x,y):
returnx+y
@logged(level=logging.CRITICAL,name='example')
defspam():
print('Spam!')
這種原理也比較好理解,看例子1,我們知道這等價于logged(add),第一個參數是函數,所以直接返回??蠢?,等價于logged(level=logging.CRITICAL, name='example')(spam),logged的第一個參數func是None(如果是裝飾器語法,第一個參數會默認傳入函數,而這里直接是函數調用,并非裝飾器。所以會返回一個partial函數,這個partial函數是真正的裝飾器,然后再走后面裝飾器的路子。其實,這種方法也是利用了“判斷傳入裝飾器的參數”。
參考資料
總結
以上是生活随笔為你收集整理的python if后面要不要加括号_Python装饰器兼容加括号与不加括号的写法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle split函数用法_「干货
- 下一篇: pythongui选哪个方案好_谈谈py