假设函数query
的定义如下:
def query(how='Here or there'):
print 'Would you like them\n%s?' % how
然后,当(无效)表达式
query(how='With a mouse', who='Daniel', where='Anywhere')
被评估,Python自动发出错误
Traceback (most recent call last):
File "/tmp/seuss.py", line 4, in <module>
query(how='With a mouse', who='Daniel', where='Anywhere')
TypeError: query() got an unexpected keyword argument 'who'
现在,假设我需要定义一个带有零个或多个位置参数的函数reply
,以及可选的命名参数prefix
。 IOW,下面的所有表达式都应该是有效的,至少在语法上是这样的:
reply('in a box', 'with a fox')
reply('In a house', 'With a mouse', preamble='I would not like them\n')
reply(preamble='I do not like them, Sam-i-am.')
我知道实现这样一个功能的唯一方法是使用签名(*args, **kwargs)
;不幸的是,有了这个签名,就reply('green eggs', wot='and', wotelse='ham')
而言,无效的表达式就Python来说都很好,这意味着我们必须自己进行错误检查。
由于这种错误检查非常公式化且乏味,因此将其重构为自己独立的模块是有意义的。例如 1 :
# check.py
def unexpected_kwargs(kwargs):
unexpected = kwargs.keys()
if len(unexpected) > 0:
import inspect
caller = inspect.getframeinfo(inspect.currentframe(1)).function
raise TypeError("%s() got an unexpected keyword argument '%s'" %
(caller, unexpected[0]))
现在我们可以像这样实现reply(*args, **kwargs)
:
import check
def reply(*args, **kwargs):
preamble = kwargs.pop('preamble', 'Not ')
check.unexpected_kwargs(kwargs)
# rest of function definition
... reply('green eggs', wot='and', wotelse='ham')
的评估结果为
Traceback (most recent call last):
File "/tmp/seuss.py", line 14, in <module>
reply('green eggs', wot='and', wotelse='ham')
File "/tmp/seuss.py", line 9, in reply
check.unexpected_kwargs(kwargs)
File "/tmp/check.py", line 8, in unexpected_kwargs
(frameinfo.function, unexpected[0]))
TypeError: reply() got an unexpected keyword argument 'wot'
这看起来类似于前面显示的错误输出,但请注意,在该输出中,违规行(query(how='With a mouse', who='Daniel', where='Anywhere')
)在回溯的最后打印,而在第二个输出中,打印出几个错误上面的线。在违规行之后,其余的追溯是IMO不必要的噪音,只会掩盖导致失败的错误。
IOW,真的复制Python对意外关键字的处理,错误输出应如下所示:
Traceback (most recent call last):
File "/tmp/seuss.py", line 14, in <module>
reply('green eggs', wot='and', wotelse='ham')
TypeError: reply() got an unexpected keyword argument 'wot'
当然,实现这一目标的一种错误方法是:
def unexpected_kwargs_NO_GOOD(kwargs):
unexpected = kwargs.keys()
if len(unexpected) > 0:
import inspect
import traceback
import sys
print >> sys.stderr, ('Traceback (most recent call last):\n%s' %
''.join(traceback.format_stack()[:-2])),
frameinfo = inspect.getframeinfo(inspect.currentframe(1))
print >> sys.stderr, ('TypeError: '
"%s() got an unexpected keyword argument '%s'" %
(frameinfo.function, unexpected[0]))
sys.exit(1)
这会产生正确的输出,但它不会引发上游代码可以捕获的异常。相反,它无条件地终止程序的执行。
有没有办法获得所需的输出,同时提出适当的异常?
1 可以分解出更多的参数处理而不仅仅是错误处理,但由于这个附加功能与帖子的问题相关,所以我把它保留了上面的示例代码。
答案 0 :(得分:2)
如果您已经确切地知道要接受哪些关键字参数,那么您根本不想使用**kwargs
。您只想使用普通的旧关键字参数。
您似乎正在搜索在Python 3中添加的功能(请参阅PEP 3102)以支持“仅关键字”参数:永远不会被位置参数自动填充的参数。< / p>
>>> def reply(*args, preamble='Not '):
... print(args)
... print(preamble)
...
>>> reply('arg1', 'arg2')
('arg1', 'arg2')
Not
>>> reply('arg1', 'arg2', preamble='yeah')
('arg1', 'arg2')
yeah
>>> reply('arg1', 'arg2', preamble='yeah', wot='wat')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-b16c25a18626> in <module>()
----> 1 reply('arg1', 'arg2', preamble='yeah', wot='wat')
TypeError: reply() got an unexpected keyword argument 'wot
Python 2中不可能提供您想要的内容.Python 2代码中的常用模式只是使用**kwargs
,而清楚地记录文档字符串中接受的选项。通常我们只是忽略发送的任何虚假参数,它们不会引发错误。
答案 1 :(得分:1)
你不能在Python引发它的同一点上引发异常,不。当有更多的关键字参数可以处理时,只需引发一个异常。
那应该没事;所有调试错误函数调用的信息仍然存在于回溯中。
答案 2 :(得分:0)
我不赞成所有这些原型检查和参数魔法,因为它违反了KISS原则,但我认为有一种方法可以实现(几乎)你想要的确切行为:
def reply(*args, **kwargs):
try:
my_arg_check(args, kwargs, *other_parameters)
except MyArgCheckFailure as exc:
raise TypeError(exc.my_custom_message)