
前言
裝飾器在日志、緩存等應(yīng)用中有廣泛使用,我們首先從之前講解的閉包為出發(fā)點(diǎn),給大家講解裝飾器的那些事。
簡(jiǎn)單的裝飾器
首先我們先復(fù)習(xí)下閉包,可以看做是函數(shù)的嵌套,而且函數(shù)內(nèi)部返回的是函數(shù)對(duì)象。
def nth(exponent):
def exponent_of(base):
return base ** exponent
return exponent_of
square = nth(2)
cube = nth(3)
print(square(5))
print(cube(5))
# 25 5^2
# 125 5^3
這里閉包外部傳入的是具有具體值的參數(shù),如果是傳入的是一個(gè)函數(shù)對(duì)象了?那就完成了一個(gè)簡(jiǎn)單的裝飾器。
def decorator(func):
def wrapper():
print('start to decorate')
func()
print('start to decorate')
return wrapper
def test():
print('welcome to decorate')
test1 = decorator(test)
test1()
#start to decorate
#welcome to decorate
#start to decorate
這段代碼中,test1變量指向了函數(shù)wrapper(),而內(nèi)部函數(shù)wrapper()又調(diào)用了test()函數(shù),因此最后打印了三段文字。
通過@語法糖,可以將上面的代碼寫的更簡(jiǎn)潔、更優(yōu)雅:
def decorator(func):
def wrapper():
print('start to decorate')
func()
print('start to decorate')
return wrapper
@decorator
def test():
print('welcome to decorate')
test()
#start to decorate
#welcome to decorate
#start to decorate
這里的@decorator相當(dāng)于是test1 = decorator(test)語句。
帶有參數(shù)的裝飾器
如果test函數(shù)中需要加入?yún)?shù)的話,我們就需要在wrapper函數(shù)中加入對(duì)應(yīng)的參數(shù):
def decorator(func):
def wrapper(message):
print('start to decorate')
func(message)
print('start to decorate')
return wrapper
@decorator
def test(message):
print(message)
test('hello world')
#start to decorate
#hello world
#start to decorate
但是新的問題來了,如果有一個(gè)新的函數(shù)也需要用到decorator()裝飾器,但是他有兩個(gè)參數(shù),我們?cè)撛趺崔k了?
@decorator
def test2(a,b):
print(a+b)
那這種情況下,我們就可以使用*args 和 **kwargs。
def decorator(func):
def wrapper(*args,**kwargs):
print('start to decorate')
func(*args,**kwargs)
print('start to decorate')
return wrapper
自定義參數(shù)的裝飾器
裝飾器還可以自定義參數(shù),這里需要多一層嵌套,讓內(nèi)部函數(shù)重復(fù)運(yùn)行多次。
def repeat(num):
def decorator(func):
def wrapper(*args,**kwargs):
print('start to decorate')
for i in range(num):
func(*args,**kwargs)
print('start to decorate')
return wrapper
return decorator
@repeat(3)
def test(message):
print(message)
test('hello world')
#start to decorate
#hello world
#hello world
#hello world
#start to decorate
有趣的現(xiàn)象
使用了裝飾器后,有一個(gè)有趣的現(xiàn)象,那就是被裝飾的函數(shù)的元信息被改變了。
print(test.__name__)
# wrapper
我們也可以通過@functools.wraps(func)內(nèi)置的裝飾器,保留原函數(shù)的元信息。
import functools
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print('start to decorate')
func(*args,**kwargs)
print('start to decorate')
return wrapper
@decorator
def test(message):
print(message)
print(test.__name__)
# test
總結(jié)
本文從閉包出發(fā),講解了裝飾器的定義和使用,其實(shí)裝飾器的作用就是:通過裝飾器函數(shù),可以對(duì)原函數(shù)的功能進(jìn)行修改,而不去修改原函數(shù)代碼。