W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
你想寫一個裝飾器,既可以不傳參數(shù)給它,比如 @decorator
,也可以傳遞可選參數(shù)給它,比如 @decorator(x,y,z)
。
下面是9.5小節(jié)中日志裝飾器的一個修改版本:
from functools import wraps, partial
import logging
def logged(func=None, *, level=logging.DEBUG, name=None, message=None):
if func is None:
return partial(logged, level=level, name=name, message=message)
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
# Example use
@logged
def add(x, y):
return x + y
@logged(level=logging.CRITICAL, name='example')
def spam():
print('Spam!')
可以看到,@logged
裝飾器可以同時不帶參數(shù)或帶參數(shù)。
這里提到的這個問題就是通常所說的編程一致性問題。當我們使用裝飾器的時候,大部分程序員習慣了要么不給它們傳遞任何參數(shù),要么給它們傳遞確切參數(shù)。其實從技術(shù)上來講,我們可以定義一個所有參數(shù)都是可選的裝飾器,就像下面這樣:
@logged()
def add(x, y):
return x+y
但是,這種寫法并不符合我們的習慣,有時候程序員忘記加上后面的括號會導(dǎo)致錯誤。這里我們向你展示了如何以一致的編程風格來同時滿足沒有括號和有括號兩種情況。
為了理解代碼是如何工作的,你需要非常熟悉裝飾器是如何作用到函數(shù)上以及它們的調(diào)用規(guī)則。對于一個像下面這樣的簡單裝飾器:
# Example use
@logged
def add(x, y):
return x + y
這個調(diào)用序列跟下面等價:
def add(x, y):
return x + y
add = logged(add)
這時候,被裝飾函數(shù)會被當做第一個參數(shù)直接傳遞給 logged
裝飾器。因此,logged()
中的第一個參數(shù)就是被包裝函數(shù)本身。所有其他參數(shù)都必須有默認值。
而對于一個下面這樣有參數(shù)的裝飾器:
@logged(level=logging.CRITICAL, name='example')
def spam():
print('Spam!')
調(diào)用序列跟下面等價:
def spam():
print('Spam!')
spam = logged(level=logging.CRITICAL, name='example')(spam)
初始調(diào)用 logged()
函數(shù)時,被包裝函數(shù)并沒有傳遞進來。因此在裝飾器內(nèi),它必須是可選的。這個反過來會迫使其他參數(shù)必須使用關(guān)鍵字來指定。并且,但這些參數(shù)被傳遞進來后,裝飾器要返回一個接受一個函數(shù)參數(shù)并包裝它的函數(shù)(參考9.5小節(jié))。為了這樣做,我們使用了一個技巧,就是利用 functools.partial
。它會返回一個未完全初始化的自身,除了被包裝函數(shù)外其他參數(shù)都已經(jīng)確定下來了。可以參考7.8小節(jié)獲取更多 partial()
方法的知識。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: