一、裝飾器概念
1、裝飾器
- 裝飾器:一種返回值也是一個函數的函數,即裝飾器。
2、裝飾器目的
-
裝飾器的目的:裝飾器是用于拓展原函數的一種函數,可以在不改變原來函數名或類名的情況下,給原函數增加新的功能。
一般情況下,我們需要改變原函數,直接改原函數的代碼即可,但是實際情況中,并不是所有的函數代碼(如核心代碼)是不允許隨意更改的,此時使用裝飾器就更為方便。
二、如何創建裝飾器
- 舉個栗子,如有一簡單函數
def function1():
print('i am a funcation!')
- 現需要將該函數運行時記錄日志,在該函數里加上打印日志的代碼即可
def function1():
print('i am a function!')
print('function1 is running!')
- 如果有大批函數都需要加上打印日志的功能,則無法一個一個去改代碼,最好能使用一個方法,給所有函數調用。
def log(func):
print('%s is runnning!' % func.__name__)
return func
@log
def function1():
print('i am a function!')
function1()
>>>
function1 is runnning!
i am a function!
- 但是裝飾器要求返回的是一個函數對象,所以我們可以將上述代碼改造一下,使用嵌套函數,使其返回一個函數對象。
def log(func):
def _decoration():
print('%s is runnning!' % func.__name__)
func()
return _decoration
@log
def function1():
print('i am a function!')
function1()
>>>
function1 is runnning!
i am a function!
三、python內置裝飾器
@property
- @property:上篇文章python類的三大特性——封裝有寫到,property,屬性的意思,被修飾的方法,可以使用引用類中字段屬性的方法去調用。
@staticmethod
- @staticmethod:靜態方法,即將類中的方法修飾為靜態方法,在不創建實例的情況下,可以通過類名直接引用。其成員方法區別是沒有self參數。
@classmethod
- @classmethod:類方法,類方法的第一個參數是一個類,將類本身作為操作的方法。
四、裝飾器類型
1、無參數的裝飾器
見上例裝飾器
2、帶固定參數的裝飾器
def log(func):
def _decoration(a, b):
print('%s is runnning!' % func.__name__)
func(a, b)
return _decoration
@log
def function1(a, b):
s = a + b
print('the result is %d' % s)
print('i am a function!')
function1(1, 2)
>>>
function1 is runnning!
the result is 3
i am a function!
3、無固定參數的裝飾器
def log(func):
def _decoration(*args, **kwargs):
print('%s is runnning!' % func.__name__)
func(*args, **kwargs)
return _decoration
@log
def function1(a, b):
s = a + b
print('the result is %d' % s)
print('i am a function!')
@log
def function2(a, b, c):
s = a + b - c
print('the result is %d' % s)
print('i am a function!')
function1(1, 2)
function2(1, 2, 3)
>>>
function1 is runnning!
the result is 3
i am a function!
function2 is runnning!
the result is 0
i am a function!
4、使用@functools.wraps
栗子如下:
def log(func):
def _decoration():
print('%s is runnning!' % func.__name__)
func()
return _decoration
@log
def function1():
print('i am a function!')
print(function1.__name__)
function1()
>>>
function1 is runnning!
i am a function!
_decoration
發現此處打印的不是function1的函數名.
使用上述代碼的缺點就是原函數的信息不見了. 如: name, 參數列表等.
所以這里引入@functools.wraps
import functools
def log(func):
@functools.wraps(func)
def _decoration():
print('%s is runnning!' % func.__name__)
func()
return _decoration
@log
def function1():
print('i am a function!')
print(function1.__name__)
function1()
>>>
function1 is runnning!
i am a function!
function1
5、類裝飾器
import functools
class Log(object):
def __call__(self, func):
@functools.wraps(func)
def _decoration():
print('%s is runnning!' % func.__name__)
func()
return _decoration
@Log()
def function1():
print('i am a function!')
print(function1.__name__)
function1()
>>>
function1 is runnning!
i am a function!
function1
6、多個裝飾器的調用順序
多個類裝飾一個函數時,調用時有一定的順序 . 簡單來說,就是裝飾器調用順序與@ 聲明的順序相反.
五、裝飾器的用途
裝飾器可以在不改變原函數功能的情況下,給原函數實現其他功能,同時,又能復用大量代碼,減少冗余.
它常被用于有切面需求的場景, 如: 打印日志, 記錄時間, 權限校驗, 事務處理, 緩存等場景.