python3数据类与** kwargs(星号)

问题描述 投票:1回答:1

目前我使用这样的DTO(数据传输对象)。

class Test1:
    def __init__(self, 
        user_id: int = None,
        body: str = None):
        self.user_id = user_id
        self.body = body

示例代码非常小,但是当对象规模增长时,我必须定义每个变量。

在挖掘它的同时,发现python 3.7支持dataclass

下面的代码是DTO使用的dataclass。

from dataclasses import dataclass


@dataclass
class Test2:
    user_id: int
    body: str

在这种情况下,我如何允许传递更多未定义到class Test2的参数?

如果我使用Test1,很容易。只需将**kwargs(asterisk)添加到__init__

class Test1:
    def __init__(self, 
        user_id: int = None,
        body: str = None,
        **kwargs):
        self.user_id = user_id
        self.body = body

但是使用dataclass,找不到任何实现它的方法。

这里有解决方案吗?

谢谢。


编辑

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

在dataclass中使用星号的原因是相同的。

我必须将字典类型传递给类init。

这有什么想法吗?

python python-3.7 python-dataclasses
1个回答
3
投票

数据类的基本用例是提供一个将参数映射到属性的容器。如果您有未知参数,则无法在创建类时知道相应的属性。

如果您在初始化期间通过手动将它们发送到catch-all属性而知道哪些参数未知,则可以解决此问题:

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不起作用。


如果您关心按名称访问属性,或者如果您在初始化期间无法区分已知和未知的参数,那么您最后的手段如果没有重写__init__(这首先打败了使用dataclasses的目的)就是写一个@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'
© www.soinside.com 2019 - 2024. All rights reserved.