带有** kwargs(星号)的python3数据类 [英] python3 dataclass with **kwargs(asterisk)
问题描述
目前,我像这样使用DTO(数据传输对象).
Currently I used DTO(Data Transfer Object) like this.
class Test1:
def __init__(self,
user_id: int = None,
body: str = None):
self.user_id = user_id
self.body = body
示例代码很小,但是当对象规模增长时,我必须定义每个变量.
Example code is very small, But when object scale growing up, I have to define every variable.
在深入研究时,发现python 3.7支持dataclass
While digging into it, found that python 3.7 supported dataclass
下面的代码是DTO使用的数据类.
Below code is DTO used dataclass.
from dataclasses import dataclass
@dataclass
class Test2:
user_id: int
body: str
在这种情况下,如何允许将未定义的更多参数传递给class Test2
?
In this case, How can I allow pass more argument that does not define into class Test2
?
如果我使用Test1
,这很容易.只需将**kwargs(asterisk)
添加到__init__
If I used Test1
, it is easy. Just add **kwargs(asterisk)
into __init__
class Test1:
def __init__(self,
user_id: int = None,
body: str = None,
**kwargs):
self.user_id = user_id
self.body = body
但是使用数据类,找不到实现它的任何方法.
But using dataclass, Can't found any way to implement it.
这里有什么解决办法吗?
Is there any solution here?
谢谢.
编辑
class Test1:
def __init__(self,
user_id: str = None,
body: str = None):
self.user_id = user_id
self.body = body
if __name__ == '__main__':
temp = {'user_id': 'hide', 'body': 'body test'}
t1 = Test1(**temp)
print(t1.__dict__)
结果:{'user_id': 'hide', 'body': 'body test'}
如您所知,我要插入字典类型为-> **temp
As you know, I want to insert data with dictionary type -> **temp
在数据类中使用星号的原因相同.
Reason to using asterisk in dataclass is the same.
我必须将字典类型传递给init类.
I have to pass dictinary type to class init.
这里有什么主意吗?
推荐答案
数据类的基本用例是提供一个将参数映射到属性的容器.如果您有未知的参数,则在类创建过程中将不知道各自的属性.
The basic use case for dataclasses is to provide a container that maps arguments to attributes. If you have unknown arguments, you can't know the respective attributes during class creation.
如果您在初始化期间知道哪些参数未知,可以手动解决该问题,方法是将其手动发送给一个包罗万象的属性:
You can work around it if you know during initialization which arguments are unknown by sending them to a catch-all attribute by hand:
from dataclasses import dataclass, field
@dataclass
class Container:
user_id: int
body: str
meta: field(default_factory=dict)
# usage:
obligatory_args = {'user_id': 1, 'body': 'foo'}
other_args = {'bar': 'baz', 'amount': 10}
c = Container(**obligatory_args, meta=other_args)
print(c.meta['bar']) # prints: 'baz'
但是在这种情况下,您仍然需要查看字典,并且无法按名称访问参数,即c.bar
不起作用.
But in this case you'll still have a dictionary you need to look into and can't access the arguments by their name, i.e. c.bar
doesn't work.
如果您关心按名称访问属性,或者在初始化期间无法区分已知参数和未知参数,那么您最后的选择就是不重写__init__
(这在很大程度上违反了使用dataclasses
的目的)第一名)正在写@classmethod
:
If you care about accessing attributes by name, or if you can't distinguish between known and unknown arguments during initialisation, then your last resort without rewriting __init__
(which pretty much defeats the purpose of using dataclasses
in the first place) is writing a @classmethod
:
@dataclass
class Container:
user_id: int
body: str
@classmethod
def from_kwargs(cls, **kwargs):
# split the kwargs into native ones and new ones
native_args, new_args = {}, {}
for name, val in kwargs.items():
if name in cls.__annotations__:
native_args[name] = val
else:
new_args[name] = val
# use the native ones to create the class ...
ret = cls(**native_args)
# ... and add the new ones by hand
for new_name, new_val in new_args.items():
setattr(ret, new_name, new_val)
return ret
# usage:
params = {'user_id': 1, 'body': 'foo', 'bar': 'baz', 'amount': 10}
Container(**params) # still doesn't work, raises a TypeError
c = Container.from_kwargs(**params)
print(c.bar) # prints: 'baz'
这篇关于带有** kwargs(星号)的python3数据类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!