見出し画像

【python3】引数違いのコンストラクタを作ろうとしてみた

結論から言うと引数違いのコンストラクタそのものは作れませんでしたが、やりたかったことは出来ました。

やりたかったこと

・あるオブジェクトのリストを入れておく、ラッパークラスが欲しい。(例えば、「本」オブジェクトを入れておく「本棚」クラス)
・「本」オブジェクトは、DBから取ってきた生データから生成する。DBから取ってきた生データ(jsonとか)をまるっと渡してインスタンスを生成すると、内部で生データを分割して「本」オブジェクトのリストを作り、保持する
・ラッパークラス内に、「自クラスを分割して、自身のインスタンスを返すメソッド」を作りたい

こいつの、2番目と3番目が競合するわけです。

自クラス内に既にある本オブジェクトのリストを渡しても、生データを渡しても、「本」オブジェクトのリストを持つラッパークラスになって欲しい。

javaなら引数違いのコンストラクタ作れば良いんですけど、pythonってそれできたっけ?と思って調べたら、作れないんですってね。

自クラスのインスタンスを返すclassmethodを使おう

class BookList():
    def __init__(self, book_list):
        self.__book_list = book_list
    
    @classmethod
    def from_json(cls, json):
      book_list = [ Book(line) for line in json]
      return cls(book_list)

まず、インスタンスは「本」オブジェクトのリストを受け取って保持するだけにしておきます。

そして、クラスメソッド(インスタンスを作らなくてもアクセスできるメソッド)を作り、生データを加工して「本」オブジェクトのリストを作り、自分で作った「本」オブジェクトのリストを引数に渡して自分自身のインスタンスを作って返します。

あれです、datetime.now()みたいな感じです。

(自身のインスタンスを返す)クラスメソッドを作る時は、

・defの上の行に@classmethodアノテーションを付与する
・第一引数にクラス自身を受け取る(一般的にclsで受け取る)
・自身のインスタンスを返すときは、cls(コンストラクタの引数)でできる(clsに自分自身のクラスが入っている)

という部分を押さえておけばOK

追記・もういっこ思いついた

コンストラクタの中で、引数で受け取ったのが本クラスなのか生データなのか判別して処理する方法もあった。

でもJava触っちゃった後だと、一つの関数の引数にどの型が入ってくるかわかんない作りは怖いなぁ~~~!って思っちゃうので、自分で書く分には上記のクラスメソッド使う方法を使いたい。

コンストラクタとクラスメソッドの使い方

「本」オブジェクトのリストが手元にあるとき(自身のインスタンスを返す時)は

books = [Book,Book,Book...] 
book_list = BookList(books)

でインスタンスを作り、生データから作りたい時は、

json = {title1:{....},title2{....}}
book_list = BookList.from_json(json)

でインスタンスを作るようにすれば、生データからでも、加工済みのデータからでもインスタンスをつくることが出来る。

selfとclsってどう違う?

通常クラス内に定義するメソッド(インスタンスメソッド、インスタンスを作らないと使えない、アノテーションのないメソッド)の第一引数は一般的にselfを使い、「自分自身」が入ることになっています。

クラスメソッドの第一引数clsにも「クラス自身」が入ります。

どう違うの?って思うじゃないですか。

インスタンスメソッドのselfは、「インスタンス(実体)自身」が入っています。なので、self.hogeとか、self.fuga()をとかすると、「インスタンス内の」変数やメソッドにアクセスできます。

一方クラスメソッドのclsは「クラス(設計図)自身」が入っています。
なので、self.を付けてアクセスする、インスタンスを作らないと実体化しない変数やメソッドにはアクセスできません。その代わり、他のクラスメソッド(あれば)にはアクセスできますし、cls(コンストラクタの引数)を使うことでインスタンスを作ることもできます。

クラスとインスタンスって抑も何?!?!

って言う人はこんな記事探してないので読み飛ばしてもらって良いんですが、「クラスは設計図、インスタンスは実体」とか言われてもよくわかんないですよね正直。(でもそれが確かに一番近い例えではあるんですよね……)

例えば、TRPGが分かるならキャラクターシートを思い浮かべてもらえるとわかりやすいかなーと思ってるんですが、クラスはキャラクターシートのテンプレート(名前やイラストやステータスを書き込む枠だけ用意してある紙)です。
インスタンスは、テンプレートをコピーして、名前やステータスを書き込んで、「プレイヤー1さんのキャラクターシート」になったもの。もちろん、書き込むステータスを変えれば「2さんのキャラクターシート」も作れるし、「3さんのシート」も作れる。

テンプレートだけの状態のシート(クラス)から、名前、とか、HP、とかの具体的な情報を取ろうとしても無理だし、「HPを減らす」とかの処理をしようにもデータもなにも空っぽだから出来ない。

情報を書き込んで使える様にしたシート(インスタンス)からなら、名前やHPを取れるし、他の処理も出来る。

clsにはテンプレートの状態が入ってて、selfにはデータが書き込んである方が入っている。そう考えると、clsの使い方もまあまあ見えてくるような気がします。

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