はじめに
masamoriです。
今回はPythonのお話。
もう何番煎だよっていう話ですが、やっと少し理解したので備忘録的に。もし奇跡的にこのブログを読んでくださってる方で、僕が間違ってるなら指摘をお願いします。
__init__
と__new__
どっちもPythonのインスタンスを作成する際に関わってくる重要なメソッド 以下原則.
例えば
class Init: def __init__(self, value): self.value = value init1 = Init(1) init2 = Init(2) print(init1 == init2) # False, 異なるインスタンス print(init1.value) # 1, 初期化時の値 print(init2.value) # 2, 初期化時の値
となる。 このとき、インスタンス自体は生成されてるのでInitクラスの基底のobjectクラスのnewが呼ばれています。 つまり、クラス自体objectクラスを継承したものであるということが分かります。 ちなみに、objectクラスの確認の仕方は
print(Init.__bases__) # <class 'object'>
__new__
を知るために
__new__
を直接編集することは、大体どの書籍見ても推奨されていません。
しかし、シングルトンパターンのPythonでの実装例を調べてみると、__new__
を使って表現している人が多くいます。
んで、その間違った書き方は以下の通り。
class Singleton: _instance = None def __new__(cls, *args, **kwargs): print("cls_instance: ", cls._instance) if not cls._instance: # 最初のインスタンス生成時にのみ呼び出される cls._instance = super().__new__(cls) return cls._instance def __init__(self, value): self.value = value singleton1 = Singleton(1) singleton2 = Singleton(2) print(singleton1 == singleton2) # True, 同じインスタンスを指す print(singleton1.value) # 2, singleton2によって設定された値 print(singleton2.value) # 2, singleton2によって設定された値
うーん、グローバル変数的な挙動をするんですね。
これは__new__
をオーバーライドしたメソッドです。インスタンスが作られたかどうかチェックして、作成されていたらそのインスタンスを_instance変数に保持してします。二回目の呼び出しでは、インスタンスは既に作成されて保持されていますから、新たなインスタンスは作成されません。__new__
が呼び出されないからね。
というわけで、この場合は__init__
だけ呼び出されてインスタンスの値が書き換えられてしまいます。
つまりこの呼び出しの場合場合、実質init単体しか動いていないことになるってことですね。
一方、initだけを実装した場合、インスタンスはinitが呼び出されるたびに作成されます。このときnewもその回数分だけ呼ばれます。この場合、objectクラスの__new__
にはインスタンスを保持する処理は施されてないので、インスタンスの値は上書きされることはありません。
別メモリ空間に書くインスタンスが保持されるわけですね。
最後に
ちなみに、シングルトンパターンはnewを使わずに書くことができます。
class Singleton: _instance = None @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls() return cls._instance
こっちの方がシンプルで好き。