下面將依次介紹裝飾器是如何來的,裝飾器如何裝飾帶返回值的函數,如何裝飾帶參數的函數,多個裝飾器如何裝飾同一個函數以及什麼叫做帶參數裝飾器。
'''
假設我們想要對於我們寫的函數進行計時(這個函數可以很多很多個),那麼我們如何做呢?下面是一種方法
'''
import time
def timmer(function): # 這就是裝飾器函數
def inner():
start = time.time()
function() # 這就是被裝飾的函數
end = time.time()
print(end - start)
return inner # 注意這裏是返回內部函數名,不帶括號
def func(): #
print("Hello Python")
func = timmer(func)
func() # 注意這裏的func()執行的其實是inner函數
# 通過上面這種方法,我們沒有修改函數的調用方式,去在原有函數的基礎上添加了一些功能,這就是裝飾器的一種粗略的模型。
'''
裝飾器原則:開放封閉原則
封閉:對修改是封閉的(相當於禁止在函數修改任何東西,即使是添加一個變量名)
開放:對擴展是開放的
裝飾器的作用:不想修改函數的調用方式,但是還想在原來的函數前後添加功能
'''
# 在上面的方法中,我們的func函數並沒有返回值,但是通常來說,函數都是有返回值的,上面的方法並不能讓我們獲得func()函數的返回值
def timmer(function): # 這就是裝飾器函數
def inner():
start = time.time()
function() # 這就是被裝飾的函數
end = time.time()
print(end - start)
return inner # 注意這裏是返回內部函數名,不帶括號
@timmer # python中的語法糖 @裝飾器函數名 這裏@timmer就相當於執行了 func = timmer(func) @下面緊跟着的就是被裝飾函數
def func(): #
print("Hello Python")
return '你好,世界'
# func = timmer(func)
a = func() # 注意這裏的func()執行的其實是inner函數
print(a)
#-----------------------------------------------------------------------------------------------------------
# 那麼如何得到func函數的返回值呢?其實只要在inner函數裏添加一個接收func函數返回值的變量,並將該變量返回即可
def timmer(function): # 這就是裝飾器函數
def inner():
start = time.time()
ret = function() # 這就是被裝飾的函數
end = time.time()
print(end - start)
return ret
return inner # 注意這裏是返回內部函數名,不帶括號
@timmer # python中的語法糖 @裝飾器函數名 這裏@timmer就相當於執行了 func = timmer(func) @下面緊跟着的就是被裝飾函數
def func(): #
print("Hello Python")
return '你好,世界'
# func = timmer(func)
a = func() # 注意這裏的func()執行的其實是inner函數
print(a)
# 上面就是裝飾帶返回值函數的裝飾器
#-----------------------------------------------------------------------------------------------------------
# 下面是裝飾帶參數函數的裝飾器
def timmer(function): # 這就是裝飾器函數
def inner(a):
start = time.time()
ret = function(a) # 這就是被裝飾的函數
end = time.time()
print(end - start)
return ret
return inner # 注意這裏是返回內部函數名,不帶括號
@timmer # python中的語法糖 @裝飾器函數名 這裏@timmer就相當於執行了 func = timmer(func) @下面緊跟着的就是被裝飾函數
def func(a): #
print("Hello Python", a)
return '你好,世界'
# func = timmer(func)
a = func(a) # 注意這裏的func()執行的其實是inner函數
print(a)
#-----------------------------------------------------------------------------------------------------------
# 上面只是一個函數並且只有一個參數,那麼如果有兩個函數,並且是關鍵字傳參等,那麼上面這種形式就不行了,下面是這種方法的應對形式
def timmer(function): # 這就是裝飾器函數
def inner(*args, **kwargs):
start = time.time()
ret = function(*args, **kwargs) # 這就是被裝飾的函數
end = time.time()
print(end - start)
return ret
return inner # 注意這裏是返回內部函數名,不帶括號
@timmer # python中的語法糖 @裝飾器函數名 這裏@timmer就相當於執行了 func = timmer(func) @下面緊跟着的就是被裝飾函數
def func(a):
print("Hello Python", a)
return '你好,世界'
@timmer
def func1(a, b):
print("Hello Python", a, b)
return '你好'
# func = timmer(func)
a = func(1) # 注意這裏的func()執行的其實是inner函數
b = func1(1, 2)
print(a)
print(b)
#=======================================================================================================================#
'''
上面就是裝飾器的形成過程,下面是裝飾器的固定形式
'''
def wrapper(function): # 這就是裝飾器函數,function是被裝飾的函數
def inner(*args, **kwargs):
'''這裏寫被裝飾函數之前要做的事'''
ret = function(*args, **kwargs) # 這就是被裝飾的函數
'''這裏寫被裝飾函數之後要做的事'''
return ret
return inner # 注意這裏是返回內部函數名,不帶括號
@wrapper # 等價於 func = wrapper(func)
def func(a):
'''這是一個func函數'''
print("Hello Python", a)
return '你好,世界'
print(func.__name__) # __name__打印函數名,這裏打印的是inner,因爲經過裝飾器裝飾後func引用的函數已經不再是原函數了
print(func.__doc__) # 打印文檔註釋
#=======================================================================================================================#
# 對於被裝飾函數,我們不想改變其引用的函數,即我們原本的func函數在被裝飾後應該依然是func函數名指的依然是原本的函數,而不是inner函數,那麼我們應該怎麼做呢?
# 很簡單,我們只需要給inner函數使用wraps裝飾器裝飾一下即可
from functools import wraps
def wrapper(function): # 這就是裝飾器函數,function是被裝飾的函數
@wraps(function) # 帶參數的裝飾器
def inner(*args, **kwargs):
'''這裏寫被裝飾函數之前要做的事'''
ret = function(*args, **kwargs) # 這就是被裝飾的函數
'''這裏寫被裝飾函數之後要做的事'''
return ret
return inner # 注意這裏是返回內部函數名,不帶括號
@wrapper
def func(a):
'''這是一個func函數'''
print("Hello Python", a)
return '你好,世界'
print(func(1))
print(func.__name__) # __name__打印函數名,這裏打印的就是原先的func了
print(func.__doc__) # 打印 這是一個func函數
#=======================================================================================================================#
# 對於多個裝飾器裝飾一個函數時
def wrapper1(function):
print("裝飾器1")
def inner(*args, **kwargs):
print("inner1")
return function(*args, **kwargs)
return inner
def wrapper2(function):
print("裝飾器2")
def inner(*args, **kwargs):
print("inner2")
return function
return inner
@wrapper1
@wrapper2
def func():
print("func函數")
func()
# 打印的結果是: 當多個裝飾器裝飾一個函數時,執行時的順序是:最先裝飾的裝飾器,最後一個執行。它遵循了先進後出這樣一個規則
# 裝飾器2
# 裝飾器1
# inner1
# inner2
#=======================================================================================================================#
# 帶參數的裝飾器(即三層裝飾器)
FLAG = True # 設置全局變量,控制裝飾器是否工作
def wrapper_out(flag):
def wrapper(function):
def inner(*args, **kwargs):
if flag: # 當flag爲True 即全局的FLAG爲True時才運行裝飾器擴展的功能
'''這裏寫裝飾器擴展的功能'''
print("裝飾器工作區域")
rec = function(*args, **kwargs)
return rec
else:
return function(*args, **kwargs)
return inner
return wrapper
@wrapper_out(FLAG) # 這裏先運行 wrapper_out(FLAG) 相當於先調用了wrapper_out函數,在@ 即 先wrapper = wrapper_out(FLAG) 再 func = wrapper(func)
def func():
return "你好,python"
@wrapper_out(FLAG)
def func2():
return "你好,java"
print(func())
print(func2())