見出し画像

【python3】クラス内に、自身のインスタンス変数一覧を辞書形式にして返すメソッドを作ってみる

結論

def format_to_dict(self):
    return vars(self)   

ホイ!

もうちょっと詳しく説明する

from datetime import datetime
class Book:
    def __init__(self, title: str, author: str, page_count: int, price: int, publish_date: datetime):
        self.title : str = title
        self.author : str = author
        self.page_count : int = self.validate_count(page_count)
        self.price : int = price
        self.taxin :int = self.calc_taxin(price)
        self.publish_date: datetime = pubish_date
    
    def validate_count(self, count: int):
        if(count > 2000):
            raise ValueError("製本できません")
        if(count < 48):
            raise ValueError("本の要件を満たしません")
        return count
            
    def calc_taxin(self, price):
        return price * 1.1    

ってクラスがあるとします。

例えば、編集部とかが使う用のプログラムを作っているとして、筆者が送ってくれた情報やら自分で入力したデータやらを取り込んで、バリデーションしたり計算したり、色々とこねくり回して、最終的に本に関わる情報が全て詰まったbookクラスができました!

よーしこのbookクラスのデータをAPI経由でデータベースに登録するためにjsonに変換して送信する部分作っぞ!みたいな場面があると思います。

そこで一々

def format(self):
    temp = {}
    temp.setdefault("title", self.title)
    temp.setdefault("author", self.author)
    ...
    return temp

とかやるのって、ちょっとモッサリ感が否めない。(もちろん、送信前にマッピングが必要なら、どっかしらでこういう処理を書かないといけないんでしょうが、今回はクラスの内容をそのままどーんしたい)

self.__dict__を使えば簡単だけど非推奨

実は__dict__属性を使うことで超簡単に実装できます。

def format(self):
    return self.__dict__
    
// {title:"タイトル", author:"著者名", ... publish_date: datetime(2022,02,22)} が得られる

が、この方法は公式のチュートリアルによると非推奨です。

オブジェクト指向の思想的には、インスタンス変数に直接アクセスさせるのはあんまり宜しくない。ということで常にgetterを通して取得させるようにしていて、その際に何か加工をしている場合もある。が、__dict__を使っちゃうとその辺全部すっ飛ばして素のインスタンス変数に直接アクセスしちゃうから、よくない。ということだそうです。

「クラスのインスタンス変数を辞書形式にしたい」って検索すると結構いっぱい出てくるんだけど__dict__使う方法。非推奨なんですって。(まあ確かに__で始まるプロパティ使うのは、お行儀が良いとは言えないわね)

vars()を使う

その代わり、vars()関数を使います。

def format(self):
    return vars(self)

でもこの関数、引数で受け取ったオブジェクトの__dict__要素を返すんだそうです。……えっそれはいいの?と思ったんですがどうもその辺の説明が見つからないので、Q&Aサイトで見かけた「len()使うやろ、__len__じゃなくて」という言葉を信じてみます。そういうことなんだろう。

クラス内にメソッドを作るメリット

vars()を使えば

book = Book("タイトル", "", 120 , 1000)
book_dict = vars(book)

ってやれば辞書形式に変換できるけど、敢えてクラス内にメソッドとして作る事で、例えば

def format(self):
    temp_dict = vars(self)
    
    temp_date = temp_dict["publish_date"]
    temp_str = temp_date.strftie(%Y/%m/%d)
    temp_dict["publish_date"] = temp_str
    
    return temp_dict

みたいに、「一部の値はさらにjson向けのフォーマット処理を加えてから出力」って事ができるし、その処理を隠蔽しておける。使う側は常にbook.format()を喚べば正しい情報が手に入るので良き。

【追記】フォーマット処理してから出力すると飛んだ落とし穴にハマることが判明したので記事にまとめました。フォーマット処理してから書き出したい場合はこちらを参照のこと。

ところでオブジェクトが入れ子になっている場合に、再帰的に内部のオブジェクトまで全部展開する方法がないか検討しているんだけど良い方法が思いつかない。ご存じの方教えてください。

この記事が気に入ったらサポートをしてみませんか?