函数在Python中给变量的用法一样也是一等公民,也就是高阶函数(High Order Function)。所有的魔法都是由此而来。
1,起源
我们想在函数login中输出调试信息,我们可以这样做
def login():
print('in login')
def printdebug(func):
print('enter the login')
func()
print('exit the login')
printdebug(login)
这个方法讨厌的是每次调用login是,都通过printdebug来调用,但毕竟这是可行的。
2,让代码变得优美一点
既然函数可以作为返回值,可以赋值给变量,我们可以让代码优美一点。
def login():
print('in login')
def printdebug(func):
def __decorator():
print('enter the login')
func()
print('exit the login')
return __decorator #function as return value
debug_login = printdebug(login) #function assign to variable
debug_login() #execute the returned function
这样我们每次只要调用debug_login就可以了,这个名字更符合直觉。我们将原先的两个函数printdebug和login绑定到一起,成为debug_login。这种耦合叫内聚:-)。
def printdebug(func):
def __decorator():
print('enter the login')
func()
print('exit the login')
return __decorator
@printdebug #combine the printdebug and login
def login():
print('in login')
login() #make the calling point more intuitive
可以看出decorator就是一个:使用函数作参数并且返回函数的函数。通过改进我们可以得到:
def printdebug(func):
def __decorator(user): #add parameter receive the user information
print('enter the login')
func(user) #pass user to login
print('exit the login')
return __decorator
@printdebug
def login(user):
print('in login:' + user)
login('jatsz') #arguments:jatsz
我们来解释一下login(‘jatsz’)的调用过程:
def printdebug(func):
def __decorator(user):
print('enter the login')
result = func(user) #recevie the native function call result
print('exit the login')
return result #return to caller
return __decorator
@printdebug
def login(user):
print('in login:' + user)
msg = "success" if user == "jatsz" else "fail"
return msg #login with a return value
result1 = login('jatsz');
print result1 #print login result
result2 = login('candy');
print result2
我们解释一下返回值的传递过程:
...omit for brief…[real][msg from login(‘jatsz’) => [result from]__decorator => [assign to] result1
def printdebug(func):
def __decorator():
print('enter the login')
func()
print('exit the login')
return __decorator
def others(func): #define a other decorator
def __decorator():
print '***other decorator***'
func()
return __decorator
@others #apply two of decorator
@printdebug
def login():
print('in login:')
@printdebug #switch decorator order
@others
def logout():
print('in logout:')
login()
print('---------------------------')
logout()
我们定义了另一个装饰器others,然后我们对login函数和logout函数分别应用这两个装饰器。应用方式很简单,在函数定义是直接用两个@@就可以了。我们看一下上面代码的输出:
$ python deoc.py
***other decorator***
enter the login
in login:
exit the login
---------------------------
enter the login
***other decorator***
in logout:
exit the login
我们看到两个装饰器都已经成功应用上去了,不过输出却不相同。造成这个输出不同的原因是我们应用装饰器的顺序不同。回头看看我们login的定义,我们是先应用others,然后才是printdebug。而logout函数真好相反,发生了什么?如果你仔细看logout函数的输出结果,可以看到装饰器的递归。从输出可以看出:logout函数先应用printdebug,打印出“enter the login”。printdebug的__decorator调用中间应用了others的__decorator,打印出“***other decorator***”。其实在逻辑上我们可以将logout函数应用装饰器的过程这样看(伪代码):