【发布时间】:2019-01-08 22:03:34
【问题描述】:
我有一个接受回调的 API 方法。回调需要一个参数。
我希望此方法将第二个参数传递给接受它的回调。但是,我必须保持与只接受原始参数的回调的兼容性。 (事实上,我预计大多数用户不会关心额外的参数,所以强迫他们明确忽略它会很烦人。)
我知道这可以使用inspect 来完成。我想知道是否有一个“惯用”或常用的解决方案不是那么重量级。
【问题讨论】:
标签: python python-3.x python-3.3
我有一个接受回调的 API 方法。回调需要一个参数。
我希望此方法将第二个参数传递给接受它的回调。但是,我必须保持与只接受原始参数的回调的兼容性。 (事实上,我预计大多数用户不会关心额外的参数,所以强迫他们明确忽略它会很烦人。)
我知道这可以使用inspect 来完成。我想知道是否有一个“惯用”或常用的解决方案不是那么重量级。
【问题讨论】:
标签: python python-3.x python-3.3
我认为您可以使用 __code__ 来查看回调需要多少参数。
if callback.__code__.co_argcount == 2:
callback(arg1, arg2)
else:
callback(arg1)
此代码未经测试,但应该可以工作。
【讨论】:
__code__ 属性。
try-except 解决方案的效率要低得多,因为它会在创建异常和回溯对象时增加开销。
__code__.co_argcount。看起来您可以进一步简化/概括它:callback(*args[:callback.__code__.co_argcount]).
更简单的解决方案是使用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 将被第二次调用。
callback 可能会产生一个无法区分的 TypeError 与相同的消息。 (诚然,这些问题在实践中不太可能出现。)
inspect.Signature.bind。您可以在尝试运行回调之前捕获 TypeError。
func_name 替换为使用此代码的函数的名称。
使用函数包装器:
from 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])
【讨论】: