9.11 è£…é¥°å™¨ä¸ºè¢«åŒ…è£…å‡½æ•°å¢žåŠ å�‚数¶
问题¶
ä½ æƒ³åœ¨è£…é¥°å™¨ä¸ç»™è¢«åŒ…è£…å‡½æ•°å¢žåŠ é¢�外的å�‚数,但是ä¸�能影å“�这个函数现有的调用规则。
解决方案¶
å�¯ä»¥ä½¿ç”¨å…³é”®å—å�‚æ•°æ�¥ç»™è¢«åŒ…è£…å‡½æ•°å¢žåŠ é¢�外å�‚数。考虑下é�¢çš„装饰器:
from functools import wraps
def optional_debug(func):
@wraps(func)
def wrapper(*args, debug=False, **kwargs):
if debug:
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
>>> @optional_debug
... def spam(a,b,c):
... print(a,b,c)
...
>>> spam(1,2,3)
1 2 3
>>> spam(1,2,3, debug=True)
Calling spam
1 2 3
>>>
讨论¶
通过装饰器æ�¥ç»™è¢«åŒ…è£…å‡½æ•°å¢žåŠ å�‚æ•°çš„å�𿳕并ä¸�常è§�。 尽管如æ¤ï¼Œæœ‰æ—¶å€™å®ƒå�¯ä»¥é�¿å…�一些é‡�å¤�代ç �ã€‚ä¾‹å¦‚ï¼Œå¦‚æžœä½ æœ‰ä¸‹é�¢è¿™æ ·çš„代ç �:
def a(x, debug=False):
if debug:
print('Calling a')
def b(x, y, z, debug=False):
if debug:
print('Calling b')
def c(x, y, debug=False):
if debug:
print('Calling c')
é‚£ä¹ˆä½ å�¯ä»¥å°†å…¶é‡�æž„æˆ�è¿™æ ·ï¼š
from functools import wraps
import inspect
def optional_debug(func):
if 'debug' in inspect.getargspec(func).args:
raise TypeError('debug argument already defined')
@wraps(func)
def wrapper(*args, debug=False, **kwargs):
if debug:
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
@optional_debug
def a(x):
pass
@optional_debug
def b(x, y, z):
pass
@optional_debug
def c(x, y):
pass
è¿™ç§�实现方案之所以行得通,在于强制关键å—å�‚æ•°å¾ˆå®¹æ˜“è¢«æ·»åŠ åˆ°æŽ¥å�— *args å’Œ **kwargs å�‚数的函数ä¸ã€‚
通过使用强制关键å—å�‚数,它被作为一个特殊情况被挑选出æ�¥ï¼Œ
并且接下æ�¥ä»…仅使用剩余的ä½�置和关键å—å�‚数去调用这个函数时,这个特殊å�‚数会被排除在外。
也就是说,它并ä¸�会被纳入到 **kwargs ä¸åŽ»ã€‚
还有一个难点就是如何去处ç�†è¢«æ·»åŠ çš„å�‚数与被包装函数å�‚数直接的å��å—冲çª�。
例如,如果装饰器 @optional_debug 作用在一个已�拥有一个 debug �数的函数上时会有问题。
è¿™é‡Œæˆ‘ä»¬å¢žåŠ äº†ä¸€æ¥å��å—æ£€æŸ¥ã€‚
上é�¢çš„æ–¹æ¡ˆè¿˜å�¯ä»¥æ›´å®Œç¾Žä¸€ç‚¹ï¼Œå› 为精明的程åº�员应该å�‘现了被包装函数的函数ç¾å��其实是错误的。例如:
>>> @optional_debug
... def add(x,y):
... return x+y
...
>>> import inspect
>>> print(inspect.signature(add))
(x, y)
>>>
通过如下的修改,�以解决这个问题:
from functools import wraps
import inspect
def optional_debug(func):
if 'debug' in inspect.getargspec(func).args:
raise TypeError('debug argument already defined')
@wraps(func)
def wrapper(*args, debug=False, **kwargs):
if debug:
print('Calling', func.__name__)
return func(*args, **kwargs)
sig = inspect.signature(func)
parms = list(sig.parameters.values())
parms.append(inspect.Parameter('debug',
inspect.Parameter.KEYWORD_ONLY,
default=False))
wrapper.__signature__ = sig.replace(parameters=parms)
return wrapper
é€šè¿‡è¿™æ ·çš„ä¿®æ”¹ï¼ŒåŒ…è£…å�Žçš„函数ç¾å��就能æ£ç¡®çš„æ˜¾ç¤º debug å�‚æ•°çš„å˜åœ¨äº†ã€‚例如:
>>> @optional_debug
... def add(x,y):
... return x+y
...
>>> print(inspect.signature(add))
(x, y, *, debug=False)
>>> add(2,3)
5
>>>
å�‚考9.16å°�节获å�–更多关于函数ç¾å��的信æ�¯ã€‚