【Python 進階】裝飾器

Python 裝飾器

什麼是裝飾器

裝飾器就是把一個函數當參數傳到另一個函數(也可以是可迭代對象 callable)中,然後再回調, @ 是其語法糖

常見的四種形態的裝飾器

1. 不帶參數的裝飾器

def logger(func):
    def wrapper(*args, **kwargs):
        print(f'prepare to run {func.__name__}')
        func(*args, **kwargs)
        print('finished...')

    return wrapper


@logger
def add(x, y):
    print(f'{x} + {y} = {x+y}')


add(1, 2)

2. 帶參數的裝飾器

def logger(level='INFO'):
    def wrapper(func):
        def handle(*args, **kwargs):
            print(f'{level}: prepare to run {func.__name__}')
            func(*args, **kwargs)
            print('finished...')
        return handle
    return wrapper


@logger('ERROR')
def add(x, y):
    print(f'{x} + {y} = {x+y}')

add(1, 2)

3. 不帶參數的類裝飾器

不帶參數時,初始化的參數爲被裝飾函數,即兩層結構。

class logger(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f'{self.level}: prepare to run {func.__name__}')
        return self.func(*args, **kwargs)


@logger
def add(x, y):
    print(f'{x} + {y} = {x+y}')

add(1, 2)

4. 帶參數的類裝飾器

帶參數時,初始化的參數爲裝飾函數的參數,即三層結構。

class logger(object):
    def __init__(self, level="INFO"):
        self.level = level

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f'{self.level}: prepare to run {func.__name__}')
            return func(*args, **kwargs)

        return wrapper

    
@logger('ERROR')
def add(x, y):
    print(f'{x} + {y} = {x+y}')

add(1, 2)

使用裝飾器的注意事項

1. 使用 functools 中的 wraps

保持被裝飾函數簽名, 否則簽名爲裝飾器對象

from functools import wraps

def wrapper(func):
    @wraps(func) 
    def handle():
        pass
    return handle

@wrapper
def wrapped():
    pass

print(wrapped.__name__)
# wrapped

2. 裝飾順序

執行順序: wrapper1 > wrapper2 (wrapper1 裝飾 wrapper2, wrapper2 裝飾 func)

@wrapper1
@wrapper2
def func():
    pass

3. 閉包

def counter(func):
    def wrapper(*args, **kwargs):
        wrapper.count = wrapper.count + 1
        res = func(*args, **kwargs)
        print("{0} has been used: {1}x".format(func.__name__, wrapper.count))
        return res

    wrapper.count = 0
    return wrapper


@counter
def system_out(string):
    print(string)
    

system_out("A")
system_out("B")
# A
# system_out has been used: 1x
# B
# system_out has been used: 2x

裝飾器常用場景及代碼實現

日誌打印,權限控制,處理函數超時(待更新)

參考

https://stackoverflow.com/que...

https://zhuanlan.zhihu.com/p/...

https://zhuanlan.zhihu.com/p/...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章