Pythonにおけるクラスのオブジェクトの生成は2段階のプロセスです:
__new__という静的メソッドが呼び出され、新しいクラスインスタンスを割り当てて返します。__init__が呼び出されます。__new__ - 作成(新しいオブジェクトを返すことができる;必須パラメータはクラス)。__init__ - 初期化(すでに割り当てられたインスタンスに対してselfで動作する)。__new__は不変タイプ(tuple、str、int)のサブクラスやシングルトン、ファクトリーパターンを作成するためにオーバーライドされます。class MyStr(str): def __new__(cls, value): print("__new__が呼び出されました") instance = super().__new__(cls, value.upper()) # 作成前に値を変更 return instance def __init__(self, value): print("__init__が呼び出されました", value) s = MyStr('abc') # __new__が呼び出されました -> __init__が呼び出されました abc print(s) # 'ABC'
クラスに__new__が実装されていない場合、不変オブジェクトを__init__で初期化することはできますか?
いいえ!例えば、不変タイプ(str、int、tupleなど)において、フィールドや値に対する変更は__new__で行う必要があり、そうでなければ__init__内で値を変更することはできません。
class MyTuple(tuple): def __init__(self, items): print(f'__init__! {items}') def __new__(cls, items): print(f'__new__! {items}') return super().__new__(cls, map(str, items)) t = MyTuple([1, 2, 3]) print(t) # ('1', '2', '3')
物語
大規模なDjangoプロジェクトで、特別な種類の文字列を保持するためにstrからの継承を実装しました。__init__内で値を変更しようとしましたが、結果は変わらず、strは不変であるため、__new__をオーバーライドすることを学んで修正しました。
物語
シングルトンパターンを実装する際、__new__内で既存のインスタンスを返すロジックを実装するのを忘れました。クラスを数回呼び出しても新しいオブジェクトが作成され続け、メモリが断片化しました。
物語
データのシリアル化ライブラリでキャッシングの際、__init__をオーバーライドしたクラスを使用しましたが、キャッシングには__new__で同じオブジェクトを返す必要があることに気づかず、各新しい呼び出しで異なるオブジェクトが作成され、キャッシュが壊れてしまいました。