函数也是对象
要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用。既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一个参数传递或返回。同时,函数体中也可以再定义函数。
装饰器本质
可以通过编写一个纯函数的例子来还原装饰器所要做的事。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def decorator(func):
def wrap():
print ( "Doing someting before executing func()" )
func()
print ( "Doing someting after executing func()" )
return wrap
def fun_test():
print ( "func" )
fun_test = decorator(fun_test)
fun_test()
# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
|
fun_test所指向的函数的引用传递给decorator()函数
decorator()函数中定义了wrap()子函数,这个子函数会调用通过func引用传递进来的fun_test()函数,并在调用函数的前后做了一些其他的事情
decorator()函数返回内部定义的wrap()函数引用
fun_test接收decorator()返回的函数引用,从而指向了一个新的函数对象
通过fun_test()调用新的函数执行wrap()函数的功能,从而完成了对fun_test()函数的前后装饰
Python中使用装饰器
在Python中可以通过@符号来方便的使用装饰器功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def decorator(func):
def wrap():
print ( "Doing someting before executing func()" )
func()
print ( "Doing someting after executing func()" )
return wrap
@decorator
def fun_test():
print ( "func" )
fun_test()
# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
|
装饰的功能已经实现了,但是此时执行:
1
2
3
4
|
print (fun_test.__name__)
# Output:
# wrap
|
fun_test.__name__已经变成了wrap,这是应为wrap()函数已经重写了我们函数的名字和注释文档。此时可以通过functools.wraps来解决这个问题。wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
更规范的写法:
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
|
from functools import wraps
def decorator(func):
@wraps (func)
def wrap():
print ( "Doing someting before executing func()" )
func()
print ( "Doing someting after executing func()" )
return wrap
@decorator
def fun_test():
print ( "func" )
fun_test()
print (fun_test.__name__)
# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
# fun_test
|
带参数的装饰器
通过返回一个包裹函数的函数,可以模仿wraps装饰器,构造出一个带参数的装饰器。
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
|
from functools import wraps
def loginfo(info = 'info1' ):
def loginfo_decorator(func):
@wraps (func)
def wrap_func( * args, * * kwargs):
print (func.__name__ + ' was called' )
print ( 'info: %s' % info)
return func( * args, * * kwargs)
return wrap_func
return loginfo_decorator
@loginfo ()
def func1():
pass
func1()
# Output:
# func1 was called
# info: info1
@loginfo (info = 'info2' )
def func2():
pass
func2()
# Output:
# func2 was called
# info: info2
|
装饰器类
通过编写类的方法也可以实现装饰器,并让装饰器具备继承等面向对象中更实用的特性
首先编写一个装饰器基类:
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
|
from functools import wraps
class loginfo:
def __init__( self , info = 'info1' ):
self .info = info
def __call__( self , func):
@wrap
def wrap_func( * args, * * kwargs):
print (func.__name__ + ' was called' )
print ( 'info: %s' % self .info)
self .after() # 调用after方法,可以在子类中实现
return func( * args, * * kwargs)
return wrap_func
def after( self ):
pass
@loginfo (info = 'info2' )
def func1():
pass
# Output:
# func1 was called
# info: info1
|
再通过继承loginfo类,扩展装饰器的功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class loginfo_after(loginfo):
def __init__( self , info2 = 'info2' , * args, * * kwargs):
self .info2 = info2
super (loginfo_after, self ).__init__( * args, * * kwargs)
def after( self ):
print ( 'after: %s' % self .info2)
@loginfo_after ()
def func2():
pass
func2()
# Output:
# func2 was called
# info: info1
# after: info2
|
以上这篇老生常谈Python进阶之装饰器就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持服务器之家。