我经常遇到此问题:
我定义了一个python类,例如:
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
然后我有一些带有附加属性的数据,需要将其转换为该对象。
例如data={"a": 1, "b": 2, "c": 3}
如何创建对象A,而忽略诸如A(**data)
之类的语法中的数据中的任何其他属性,或者使用诸如toObject(A, **data)
之类的通用帮助方法
而不在类A的定义中包含**kwargs
。
答案 0 :(得分:3)
通常,您将只能使用hackey方法。老实说,我无法想到为什么您不能直接提供所需的参数。但是Python具有很多自省/元编程功能,因此您可以一起破解:
import inspect
def instantiate(cls, payload):
params = inspect.signature(cls).parameters
return cls(**{k:payload[k] for k in params})
my_a = instantiate(A, data)
但实际上,这只是我的主要代码气味。
您应该只使用A(data['a'],data['b'])
答案 1 :(得分:1)
如果您接受使用**kwargs
,那么它很简单:
class A:
def __init__(self, a, b, **kwargs):
self.a = a
self.b = b
# Ignore those two lines below if no need of extra args:
if kwargs.get("update", False):
self.__dict__.update(kwargs)
然后A(**data)
将忽略额外的参数,而A(**data, update=True)
将使用额外的参数(包括update
)来更新实例。
这大约是function signature,**kwargs
确保它将收集所有额外的参数并且适合任何使用或滥用,那么您将永远不会得到:
TypeError: __init__() got an unexpected keyword argument 'c'
如果您不想使用**kwargs
商品,那么您应该问自己:为什么我接受一个实例,该实例创建的参数数量错误并限制了函数签名?
总结一下,您有几个选择:
assert
定义中添加很多__init__
),然后您需要使用正确的参数设置调用您的类实例; *args
和**kwargs
的优势来设置变量签名,而不必理会那些多余的参数(如果您的数据来自另一个JSON格式的应用程序,认为我们可以说这样做是安全的); 回顾Python的禅宗:
import this
答案 2 :(得分:0)
我现在正在使用第三方库pydantic,该库专门解决了数据验证和输出一致性的问题。
在这种情况下,我要确保输入数据符合A类的签名。这可以通过以下操作实现
AModel
使用来自pydantic的BaseModel
来匹配类的输入参数。data
之前,将A(**AModel(**data).dict())
有效负载通过模型(pydantic模型的默认行为会忽略额外的属性)这不仅实现了更多的验证,同时也是一种更加严格的方法。