我有一个第三方类,它在其__init__
方法中接受许多参数。
class ThirdPartyClass:
def __init__(self, a, b, c):
pass
我希望能够实例化字典中给定多余参数的类(如下所示),由于参数d
和e
而抛出错误。
params = {"a": 1, "b": 2, "c":3, "d": 4, "e": 5}
ThirdPartyClass(**params)
我如何编写一个方便的包装器,其工作原理类似于将**kwargs
添加到ThirdPartyClass
的__init__
,而不以任何方式修改ThirdPartyClass
的源代码?
当然,我可以编写自己的ThirdPartyClass
模拟类,但我更喜欢更好的解决方案。
非常感谢!
编辑:我不想硬编码参数名称。不过,我可能可以使用inspect
来完成这项工作。此外,解决方案应该是通用的,并适用于任何外部第三方类。
如果您需要一个完全通用的解决方案,可能最优雅的方法是编写一个装饰器。这将适用于函数和类,因为它们都是可调用的(在实例化类时调用类)。您可以将具有@
语法的装饰器应用到您自己的代码中,或者将其用作包装库代码的高阶函数。
为了能够处理任意函数签名,我们将使用inspect
模块来计算参数名称。有两点需要注意:
-
如果函数已经有一个参数来接受可变关键字参数(
**kwargs
或其他名称),那么我们不需要改变任何东西,而且确实不应该改变任何东西。 -
否则,如果函数有一个参数接受可变位置参数(
*args
,或其他名称),那么它的名称在调用的参数中不可用,在过滤传入关键字参数时应排除。
:
from functools import wraps
from inspect import signature, Parameter
def ignore_extra_keywords(func):
params = signature(func).parameters.values()
if any(p.kind == Parameter.VAR_KEYWORD for p in params):
return func
names = {p.name for p in params if p.kind != Parameter.VAR_POSITIONAL}
@wraps(func)
def wrapper(*args, **kwargs):
# using `names` as a closure
return func(*args, **{k: kwargs[k] for k in (kwargs.keys() & names)})
return wrapper
现在我们可以做
ignore_extra_keywords(ThirdPartyClass)(**params)
或
@ignore_extra_keywords
def my_func(a, b, c):
print(f'I got: {a}, {b}, {c}')
my_func(1, b=2, c=3, d=4)
知道"a"、"b"one_answers"c"是好的参数吗?
class ThirdPartyClass:
def __init__(self, a, b, c):
pass
bad_params = {"a": 1, "b": 2, "c":3, "d": 4, "e": 5}
good_keys = ["a", "b", "c"]
good_params = {}
for k,v in bad_params.items():
if k in good_keys:
good_params[k] = v
ThirdPartyClass(**good_params)
在线词典:
good_params = {k:v for (k,v) in bad_params.items() if k in good_keys}
如果知道的参数,可以这样写:
{k:params[k] for k in (params.keys() & {'a','b','c'})}
返回
{'a': 1, 'b': 2, 'c': 3}
在本例中
因此,代码的效果可以是:
obj = ThirdPartyClass(**{k:params[k] for k in (params.keys() & {'a','b','c'})})
如果你不知道正确的参数,你可以使用:
import inspect
correct_parameters = set(inspect.signature(ThirdPartyClass).parameters)
就变成了
obj = ThirdPartyClass(**{k:params[k] for k in (params.keys() & correct_paramters)})