標籤:rate *args 操作 裝飾器 local icc critical efault imp
2.1.定義一個接受參數的裝飾器
前言:在理解上一篇文章的基礎上理解定義一個接受參數的裝飾器
思路:在裝飾器函數的外層再定義一個接受參數的函數,讓他返回裝飾器函數,在裝飾器函數中進行相關參數的進行操作
代碼解析如下:
from functools import wraps
import logging
# 定義外層函數logged,使用return decorate返回裝飾器函數
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.
"""
# 定義一個裝飾器,返回wrapper函數
def decorate(func):
# 對傳入的參數進行相關操作
logname = name if name else func.__module__
print(logname)
log = logging.getLogger(logname)
print(log)
logmsg = message if message else func.__name__
print(logmsg)
@wraps(func)
# 定義wrapper函數,返回被裝飾的func函數,即後面樣本中的add函數或者spam函數
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
print(add(2,5))
輸出:
__main__
<Logger __main__ (WARNING)>
add
7
@logged(logging.CRITICAL, ‘example‘)
def spam():
print(‘Spam!‘)
print(spam())
輸出:
example
<Logger example (WARNING)>
spam
Spam!
None #若def函數後面追加一個return “hello”,則最後一個None改為hello
小結:
@decorator(x, y, z)def func(a, b): pass
裝飾器處理過程跟下面的調用是等效的;
def func(a, b): passfunc = decorator(x, y, z)(func)
2.2 如何定義一個屬性可由使用者修改的裝飾器
前言:定義一個可以傳參數的裝飾器,若現在需要給以動態修改裝飾器的屬性,改若何定義呢?
思路:此處使用訪問器函數(accessor function),然後nonlocal 來修改內部變數
代碼解析如下:
from functools import wraps, partialimport logging# Utility decorator to attach a function as an attribute of objdef attach_wrapper(obj, func=None): if func is None: return partial(attach_wrapper, obj) setattr(obj, func.__name__, func) return funcdef 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) # Attach setter functions @attach_wrapper(wrapper) def set_level(newlevel): nonlocal level level = newlevel @attach_wrapper(wrapper) def set_message(newmsg): nonlocal logmsg logmsg = newmsg return wrapper return decorate# Example use@logged(logging.DEBUG)def add(x, y): return x + y@logged(logging.CRITICAL, ‘example‘)def spam(): print(‘Spam!‘)# level層級預設設定為DEBUGlogging.basicConfig(level=logging.DEBUG)print(add(2, 3))# DEBUG:__main__:add# 5# Change the log messageadd.set_message(‘Add called‘)print(add(2, 3))# DEBUG:__main__:Add called# 5# Change the log leveladd.set_level(logging.WARNING)print(add(2, 3))# WARNING:__main__:Add called# 5
若使用其他對於屬性直接存取的方法,只能訪問到頂層裝飾器,例如:
@timethis@logged(logging.DEBUG)def countdown(n): while n > 0: n -= 1
其中頂層裝飾器函數為:@timethis,用直接存取屬性的方法會失敗,請記住這點
本小節的內容可以作為後續類裝飾器的替代
python進階之裝飾器之2.定義一個可接受參數的裝飾器、如何定義一個屬性可由使用者修改的裝飾器、定義一個能接受選擇性參數的裝飾器