在不破坏现有回调的情况下将额外的可选参数传递给回调
Pass extra optional arguments to callbacks without breaking existing callbacks
我有一个接受回调的 API 方法。回调需要一个参数。
我希望此方法将第二个参数传递给接受它的回调。但是,我必须保持与只接受原始参数的回调的兼容性。 (事实上 ,我希望大多数用户不会关心额外的参数,因此强制他们明确忽略它会很烦人。)
我知道这可以使用 inspect
来完成。我想知道是否有 "idiomatic" 或不那么重量级的常用解决方案。
使用函数包装器:
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])
一个更简单的解决方案是使用 try
块尝试首先使用第二个参数调用回调,然后再回退到只使用 except
块中的一个参数调用:
try:
callback(first, second)
except TypeError as e:
if e.__traceback__.tb_frame.f_code.co_name != 'func_name':
raise
callback(first)
我认为您可以使用 __code__ 查看回调需要多少参数。
if callback.__code__.co_argcount == 2:
callback(arg1, arg2)
else:
callback(arg1)
此代码未经测试,但应该可以使用。
我有一个接受回调的 API 方法。回调需要一个参数。
我希望此方法将第二个参数传递给接受它的回调。但是,我必须保持与只接受原始参数的回调的兼容性。 (事实上 ,我希望大多数用户不会关心额外的参数,因此强制他们明确忽略它会很烦人。)
我知道这可以使用 inspect
来完成。我想知道是否有 "idiomatic" 或不那么重量级的常用解决方案。
使用函数包装器:
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])
一个更简单的解决方案是使用 try
块尝试首先使用第二个参数调用回调,然后再回退到只使用 except
块中的一个参数调用:
try:
callback(first, second)
except TypeError as e:
if e.__traceback__.tb_frame.f_code.co_name != 'func_name':
raise
callback(first)
我认为您可以使用 __code__ 查看回调需要多少参数。
if callback.__code__.co_argcount == 2:
callback(arg1, arg2)
else:
callback(arg1)
此代码未经测试,但应该可以使用。