ProgrammingシニアPython開発者

Pythonにおける__new__メソッドはどのように機能し、__init__とは何が異なりますか?__new__をオーバーライドする必要があるのはいつで、なぜですか?使用例を詳しく説明してください。

Hintsage AIアシスタントで面接を突破

回答。

Pythonにおけるクラスのオブジェクトの生成は2段階のプロセスです:

  1. まず、__new__という静的メソッドが呼び出され、新しいクラスインスタンスを割り当てて返します。
  2. 次に、すでに作成されたオブジェクトを初期化する__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__で同じオブジェクトを返す必要があることに気づかず、各新しい呼び出しで異なるオブジェクトが作成され、キャッシュが壊れてしまいました。