📖
Pythonで辞書型をクラスに変換したい!
やりたいこと
このようなdict型のデータがあったとします。
data = {
"name": "tinyowl",
"age": 5,
"language": [
"japanise",
"english",
"owl-lang"
]
}
上のデータを、自作クラスに変換したいのです。
def to_user_class(data: dict) -> User:
# なんやかんや
user1 = to_user_class(data)
print(type(user1)) # <class '__main__.User'>
print(user1.name) # tinyowl
print(user1.language[0]) # japanise
結論
調べてみたところ、以下のようなコードが一番シンプルでよさそうでした。
外部ライブラリを使う方法もあるようですが、できるだけ標準ライブラリでやりたかったので使いませんでした。
class User(object):
def __init__(self, name: str, age: int, language: list[str]):
self.name = name
self.age = age
self.language = language
def to_user_class(d: dict) -> User:
# __init__を呼ばずにインスタンスを作成
u = User.__new__(User)
u.__dict__.update(d)
return u
user1 = to_user_class(data)
print(type(user1)) # <class '__main__.User'>
print(user1.name) # tinyowl
print(user1.language[0]) # japanise
__dict__.updateが何をしているかというと、インスタンス変数の値を上書きしています。
object型は__dict__という変数を持っていて、これがインスタンス変数の状態を保持しているようです。
updateはdict型同士を結合するメソッドです。(詳細)
なので自作クラスで定義していない変数がデータの中にあった場合にも、勝手に変数が生成されてしまいます。
data = {"a": 0, "b": 2}
class Test(object):
def __init__(self, a: int):
self.a = a
test = Test.__new__(Test)
test.__dict__.update(data)
print(test.b) # 2
dataclassを使う
dataclassを使えばシンプルにクラスを実装できます。
dataclassについてはこちらの記事がわかりやすかったです。
from dataclasses import dataclass
# 上のUserクラスと中身は同じ
@dataclass
class User:
name: str
age: int
language: list[str]
初期値を設定
データに特定の要素がなかった場合、エラーが発生してしまいます。
data = {
"name": "tinyowl",
# "age": 5,
"language": [
"japanise",
"english",
"owl-lang"
]
}
user1 = to_user_class(data)
print(user1.age) # error!
AttributeError: 'User' object has no attribute 'age'
初期値を指定することでこの問題を回避できます。
@dataclass
class User:
name: str
language: list[str]
# 初期値がある変数は、初期値がない変数の後に宣言する必要があります。
age: int = 0
Noneを指定することもできます。
from typing import Optional
@dataclass
class User:
name: str
language: list[str]
age: Optional[int] = None
まとめ
僕にしか需要なさそう…?
最後に
最後まで読んでいただきありがとうございます。
Zenn初記事なので色々とおかしいところがあるかもしれません。
間違いやアドバイス等あれば、コメントでご指摘頂けるとありがたいです。
Discussion