我有一个接受回调函数的API方法。回调函数期望一个参数。
我希望这个方法传递第二个参数给那些需要它的回调函数。但是,我必须保持与仅接受原始参数的回调函数的兼容性。(事实上,我预计大多数用户都不会关心额外的参数,因此强制他们明确忽略它将是很烦人的。)
我知道可以使用 inspect
来完成这个任务。但我想知道是否有一个“惯用”的或常用的解决方案,不太重量级。
我有一个接受回调函数的API方法。回调函数期望一个参数。
我希望这个方法传递第二个参数给那些需要它的回调函数。但是,我必须保持与仅接受原始参数的回调函数的兼容性。(事实上,我预计大多数用户都不会关心额外的参数,因此强制他们明确忽略它将是很烦人的。)
我知道可以使用 inspect
来完成这个任务。但我想知道是否有一个“惯用”的或常用的解决方案,不太重量级。
if callback.__code__.co_argcount == 2:
callback(arg1, arg2)
else:
callback(arg1)
try
块,首先尝试使用第二个参数调用回调函数,然后在except
块中备用,只使用一个参数调用回调函数。try:
callback(first, second)
except TypeError as e:
if e.__traceback__.tb_frame.f_code.co_name != 'func_name':
raise
callback(first)
callback(first, second)
由于任何其他原因引发了TypeError
,那么该错误将被丢弃,并且callback
将再次被调用。 - Thom Smithcallback
可能会产生一个具有相同消息的无法区分的TypeError
。(这些问题在实践中被承认不太可能出现。) - Thom Smithinspect.Signature.bind
。在尝试运行回调函数之前,您可以捕获 TypeError
。 - Thom Smithfunc_name
替换为此代码所使用的函数的名称。 - blhsingfrom inspect import signature, Parameter
def ignore_extra_arguments(function):
positional_count = 0
var_positional = False
keyword_names = set()
var_keyword = False
for p in signature(function).parameters.values():
if p.kind == Parameter.POSITIONAL_ONLY:
positional_count += 1
elif p.kind == Parameter.POSITIONAL_OR_KEYWORD:
positional_count += 1
keyword_names.add(p.name)
elif p.kind == Parameter.VAR_POSITIONAL:
var_positional = True
elif p.kind == Parameter.KEYWORD_ONLY:
keyword_names.add(p.name)
elif p.kind == Parameter.VAR_KEYWORD:
var_keyword = True
if var_positional:
new_args = lambda args: args
else:
new_args = lambda args: args[:positional_count]
if var_keyword:
new_kwargs = lambda kwargs: kwargs
else:
new_kwargs = lambda kwargs: {
name: value for name, value in kwargs.items()
if name in keyword_names
}
def wrapped(*args, **kwargs):
return function(
*new_args(args),
**new_kwargs(kwargs)
)
return wrapped
它能够工作,但有点粗暴。
更简单的版本,假设 function
没有关键字参数或可变参数:
from inspect import signature
def ignore_simple(function):
count = len(signature(function).parameters)
return lambda *args: function(*args[:count])
__code__
属性。 - blhsingtry-except
解决方案的效率要低得多,因为它会增加异常和回溯对象的创建开销。 - blhsing__code__.co_argcount
。看起来你甚至可以更进一步地简化/概括它:callback(*args[:callback.__code__.co_argcount])
。 - Thom Smith