Pythonの特殊メソッド__new__とは
特殊メソッド __new__について
Pythonには、「特殊メソッド」と呼ばれるものが存在します。
Pythonではこの特殊メソッドをオーバーライドし使用することで、自分が定義したクラスのインスタンスの振る舞いを細かく調整することが可能です。
その特殊メソッドの一つが__new__です。
__new__はインスタンスの生成時に呼ばれる特殊メソッドです。
他にも、インスタンスに関わる特殊メソッドとして、インスタンスの初期化時に呼ばれる私たちに馴染深い__init__メソッド、インスタンスの破壊時に呼ばれる__del__メソッドがあります。
__new__を考える上で 切り離せないのが特殊メソッド__init__です。__init__と__new__の役割の違いを見ていきましょう。
__new__と __init__の違い
__init__はどちらかというと他の言語(Javaなど)のコンストラクタ的なものなので、 __init__が呼び出されるとオブジェクトの生成+初期化が行われていると思われがちですが、Pythonの場合はオブジェクトの生成はnewメソッドで公開/カスタマイズされるもので、 __init__の役割はあくまで初期化です。(Javaなどはコンストラクタが呼び出されるとオブジェクトの生成が自動で行われ、コンストラクタ内の処理で初期化が行われます。)
なので、 __new__はインスタンスの生成、__init__はインスタンスの初期化という役割に明確な違いがあります。
当たり前ですが、名前通りの働きをしてくれていることになりますね。
__new__
- 新しいインスタンスの生成時に呼ばれます。
- 第一引数にはクラス自身を表すclsをとります。
__init__
利用例
以下で __new__を使用する場合の利用例を示していきます。
1.シングルトンパターン
シングルトンパターンとはGoFのデザインパターンの一種で生成されるインスタンスを一つに制限することです。
ロケールなどの処理で使用されるデザインパターンだそうですが、使われるのはあまり見ないかもしれません。
# main.py class Singleton(): singleton = None # *argがないとSomeClassの__init__で引数を指定できない def __new__(cls, *arg, **kwargs): if cls.singleton is None: cls.singleton = super().__new__(cls) return cls.singleton class SomeClass(Singleton): def __init__(self, name: str): self.__name = name @property def name(self): return self.__name def main(): a = SomeClass('Jone') print(f'a.name is {a.name}') print(a) b = SomeClass('Ken') print(f'a.name is {a.name}') print(f'b.name is {b.name}') print(a) print(b) if __name__ == '__main__': main()
# 実行結果 a.name is Jone <__main__.SomeClass object at 0x10214a150> a.name is Ken b.name is Ken <__main__.SomeClass object at 0x10214a150> <__main__.SomeClass object at 0x10214a150>
ちゃんと生成されたインスタンスが一つだけになっています。
2.イミュータブルなクラスの継承した時の初期化
strやtuppleなどイミュータブルなクラスを継承した場合はinitでは初期化できません。
そこで__new__を使用することによって擬似的に初期化(インスタンス生成時のカスタマイズ)を行います。
strの場合の例
修正前
# strの場合 class SomeClass(str): def __init__(self, args): self = 'Jone' def main(): a = SomeClass('Ken') print(a)
# 実行結果 Ken
修正後
class SomeClass(str): def __new__(cls, args): self = str.__new__(cls, 'Jone') return self def main(): a = SomeClass('Ken') print(a)
# 実行結果 Jone
tupleの場合の例
# tupleの場合 class SomeClass(tuple): def __init__(self, args): print(self[0] + self[1] + self[2]) self[4] = 'San' def main(): SomeClass('Ken')
# 実行結果 TypeError: 'SomeClass' object does not support item assignment
class SomeClass(tuple): def __new__(cls, args): x = args[0] y = args[1] z = args[2] self = tuple.__new__(cls, (x, y, z, 'San')) return self def main(): a = SomeClass('Ken') print(a)
# 実行結果 ('K', 'e', 'n', 'San')
こちらも積極的に使うことはないと思いますが、このようなことができます。
終わりに
今回はあまり見かけない特殊メソッド__new__について調べてみましたが、上の利用例の他にもメタプログラミングなどで__new__をよく使用することがあるみたいですが、あまりまだメタプログラミングについては詳しくないので割愛します。
調べた限りでは特殊メソッド__new__はそこまで積極的に使うことはなさそうですね。