9.4 定義一個(gè)帶參數(shù)的裝飾器

2018-02-24 15:27 更新

問題

你想定義一個(gè)可以接受參數(shù)的裝飾器

解決方案

我們用一個(gè)例子詳細(xì)闡述下接受參數(shù)的處理過程。假設(shè)你想寫一個(gè)裝飾器,給函數(shù)添加日志功能,當(dāng)時(shí)允許用戶指定日志的級別和其他的選項(xiàng)。下面是這個(gè)裝飾器的定義和使用示例:

from functools import wraps
import logging

def logged(level, name=None, message=None):
    """
    Add logging to a function. level is the logging
    level, name is the logger name, and message is the
    log message. If name and message aren't specified,
    they default to the function's module and name.
    """
    def decorate(func):
        logname = name if name else func.__module__
        log = logging.getLogger(logname)
        logmsg = message if message else func.__name__

        @wraps(func)
        def wrapper(*args, **kwargs):
            log.log(level, logmsg)
            return func(*args, **kwargs)
        return wrapper
    return decorate

# Example use
@logged(logging.DEBUG)
def add(x, y):
    return x + y

@logged(logging.CRITICAL, 'example')
def spam():
    print('Spam!')

初看起來,這種實(shí)現(xiàn)看上去很復(fù)雜,但是核心思想很簡單。最外層的函數(shù) logged() 接受參數(shù)并將它們作用在內(nèi)部的裝飾器函數(shù)上面。內(nèi)層的函數(shù) decorate() 接受一個(gè)函數(shù)作為參數(shù),然后在函數(shù)上面放置一個(gè)包裝器。這里的關(guān)鍵點(diǎn)是包裝器是可以使用傳遞給 logged() 的參數(shù)的。

討論

定義一個(gè)接受參數(shù)的包裝器看上去比較復(fù)雜主要是因?yàn)榈讓拥恼{(diào)用序列。特別的,如果你有下面這個(gè)代碼:

@decorator(x, y, z)
def func(a, b):
    pass

裝飾器處理過程跟下面的調(diào)用是等效的;

def func(a, b):
    pass
func = decorator(x, y, z)(func)

decorator(x, y, z) 的返回結(jié)果必須是一個(gè)可調(diào)用對象,它接受一個(gè)函數(shù)作為參數(shù)并包裝它,可以參考9.7小節(jié)中另外一個(gè)可接受參數(shù)的包裝器例子。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號