保留裝飾功能的簽名假設(shè)我編寫了一個裝飾器來做一些非常通用的東西。例如,它可能會將所有參數(shù)轉(zhuǎn)換為特定類型,執(zhí)行日志記錄,實現(xiàn)memoization等。這是一個例子:def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
return g@args_as_intsdef funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z>>> funny_function("3", 4.0, z="5")22到目前為止一切都很好。然而,有一個問題。裝飾函數(shù)不保留原始函數(shù)的文檔:>>> help(funny_function)Help on function g in module __main__:g(*args, **kwargs)幸運(yùn)的是,有一個解決方法:def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
g.__name__ = f.__name__
g.__doc__ = f.__doc__ return g@args_as_intsdef funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z這次,函數(shù)名稱和文檔是正確的:>>> help(funny_function)Help on function funny_function in module __main__:funny_function(*args, **kwargs)
Computes x*y + 2*z但仍存在一個問題:功能簽名是錯誤的。信息“* args,** kwargs”幾乎沒用。該怎么辦?我可以想到兩個簡單但有缺陷的解決方法:1 - 在docstring中包含正確的簽名:def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z這很糟糕,因為重復(fù)。簽名仍無法在自動生成的文檔中正確顯示。更新函數(shù)很容易,忘記更改文檔字符串或打字錯誤。[ 是的,我知道docstring已經(jīng)復(fù)制了函數(shù)體這一事實。請忽略這一點(diǎn); funny_function只是一個隨機(jī)的例子。]2 - 不使用裝飾器,或為每個特定簽名使用專用裝飾器:def funny_functions_decorator(f):
def g(x, y, z=3):
return f(int(x), int(y), z=int(z))
g.__name__ = f.__name__
g.__doc__ = f.__doc__ return g這適用于具有相同簽名的一組函數(shù),但一般來說它沒用。正如我在開始時所說,我希望能夠完全使用裝飾器。我正在尋找一種完全通用且自動化的解決方案。所以問題是:有沒有辦法在創(chuàng)建裝飾函數(shù)簽名后對其進(jìn)行編輯?否則,我可以編寫一個提取器來提取函數(shù)簽名并在構(gòu)造裝飾函數(shù)時使用該信息而不是“* kwargs,** kwargs”嗎?如何提取該信息?我應(yīng)該如何構(gòu)建裝飾函數(shù) - 使用exec?還有其他方法嗎?
3 回答

陪伴而非守候
TA貢獻(xiàn)1757條經(jīng)驗 獲得超8個贊
這是通過Python的標(biāo)準(zhǔn)庫functools
和特定functools.wraps
功能來解決的,該功能旨在“?將包裝函數(shù)更新為包裝函數(shù)?”。但是,它的行為取決于Python版本,如下所示。應(yīng)用于問題的示例,代碼如下所示:
from?functools?import?wrapsdef?args_as_ints(f): ????@wraps(f)? ????def?g(*args,?**kwargs): ????????args?=?[int(x)?for?x?in?args] ????????kwargs?=?dict((k,?int(v))?for?k,?v?in?kwargs.items()) ????????return?f(*args,?**kwargs) ????return?g@args_as_intsdef?funny_function(x,?y,?z=3): ????"""Computes?x*y?+?2*z""" ????return?x*y?+?2*z
在Python 3中執(zhí)行時,會產(chǎn)生以下結(jié)果:
>>>?funny_function("3",?4.0,?z="5")22>>>?help(funny_function)Help?on?function?funny_function?in?module?__main__:funny_function(x,?y,?z=3) ????Computes?x*y?+?2*z
它唯一的缺點(diǎn)是在Python 2中,它不會更新函數(shù)的參數(shù)列表。在Python 2中執(zhí)行時,它將產(chǎn)生:
>>>?help(funny_function)Help?on?function?funny_function?in?module?__main__:funny_function(*args,?**kwargs) ????Computes?x*y?+?2*z

慕森王
TA貢獻(xiàn)1777條經(jīng)驗 獲得超3個贊
有一個裝飾器模塊,decorator
你可以使用裝飾器:
@decoratordef?args_as_ints(f,?*args,?**kwargs): ????args?=?[int(x)?for?x?in?args] ????kwargs?=?dict((k,?int(v))?for?k,?v?in?kwargs.items()) ????return?f(*args,?**kwargs)
然后保留方法的簽名和幫助:
>>>?help(funny_function)Help?on?function?funny_function?in?module?__main__:funny_function(x,?y,?z=3) ????Computes?x*y?+?2*z
添加回答
舉報
0/150
提交
取消