とりあえず
dataclassは好きです。型指定あんま役に立たないけど
でも、初期値が面倒です。特に
dataclasses.field(default_factory・・・
これ
そんなわけで、なんとかしたいと思います
@dataclasses.dataclass
class Margin:
top: int
bottom: int
left: int
right: int
al: list
@dataclasses.dataclass
class Color:
r: int
g: int
b: int
m: Margin
@dataclasses.dataclass
class Config:
margin : Margin
output_margin : Margin
corner_average : float
threshold_gray : int
threshold_color : Color
こんなdataclassを作りました。dataclass内で他のdataclassを参照してたりして、色々アレです。
そして、こうです
DefaultInitializer = {int: 0, float:0.0, str:'', list:[], dict:{}}
def DCinitializer(DataClass: type, initializer: dict = DefaultInitializer, **kwargs):
arg = {}
for k, attr in DataClass.__annotations__.items():
f = False
for at, dv in initializer.items():
if attr is at:
f = True
arg[k] = dv
for kw, dv in kwargs.items():
if kw == k:
f = True
arg[k] = dv
if not f:
sarg = {}
for kw, dv in kwargs.items():
if k == kw[:len(k)]:
sarg[kw[len(k)+1:]] = dv
arg[k] = DCinitializer(attr,**sarg)
return DataClass(**arg)
a = DCinitializer(Config,margin_top = 5)
print(a)
#結果
#Config(margin=Margin(top=5, bottom=0, left=0, right=0, al=[]),
# output_margin=Margin(top=0, bottom=0, left=0, right=0, al=[]),
# corner_average=0.0, threshold_gray=0,
# threshold_color=Color(r=0, g=0, b=0,
# m=Margin(top=0, bottom=0, left=0, right=0, al=[])))
#
それぞれの型の初期値は
DefaultInitializer = {int: 0, float:0.0, str:'', list:[], dict:{}}
で決めています。dataclassがネストしていても、遡ってそれぞれの値を入れます
クラスの初期値は別の方が良いとかなら
DefaultInitializer = {int: 0, float:0.0, str:'', list:[], dict:{}}, Margin:(10,10,10,10,['ほげ'])
とでも入れておけばそのクラスの初期値はそれになります
a = DCinitializer(Config,margin_top = 5)
の、margin_top = 5は
クラス指定されているmarginのtopプロパティが対象です
本当は
a = DCinitializer(Config,margin.top = 5)
と書きたかったのですが、それだとSyntaxErrorが出てしまうので_にしています。別に_で無くてもPythonで変数と認識してくれるものならなんでもいいです
初期値の引数はキーワード引数で無いといけませんが、素のdataclassのように全部書かなくても必要な部分だけで構いません
とりあえず思いつきで作ったので、あんまり細かい事は考えてませんが、使えるかもしれません
###追記
fix typo by @shiracamus
ご指摘ありがとうございます。
initializerの綴り間違ってた・・・
コメント欄でのご指摘もありがとうございます。短くなって良いですね