python3 データクラスと **kwargs(アスタリスク) 質問する

python3 データクラスと **kwargs(アスタリスク) 質問する

現在は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 が使用するデータクラスです。

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

しかし、データクラスを使用しても、それを実装する方法が見つかりません。

ここに解決策はあるでしょうか?

ありがとう。


編集

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

データクラスでアスタリスクを使用する理由も同じです。

辞書型をクラス init に渡す必要があります。

何かアイデアはありますか?

ベストアンサー1

データクラスの基本的な使用例は、引数を属性にマップするコンテナーを提供することです。不明な引数がある場合、クラスの作成時にそれぞれの属性を知ることはできません。

初期化中にどの引数が不明であるかがわかっている場合は、手動でキャッチオール属性に送信することで回避できます。

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

from dataclasses import dataclass
from inspect import signature


@dataclass
class Container:
    user_id: int
    body: str

    @classmethod
    def from_kwargs(cls, **kwargs):
        # fetch the constructor's signature
        cls_fields = {field for field in signature(cls).parameters}

        # split the kwargs into native ones and new ones
        native_args, new_args = {}, {}
        for name, val in kwargs.items():
            if name in cls_fields:
                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

使用法:

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'

おすすめ記事