1. ホーム
  2. python

[解決済み] Python (およびPython C API)。new__ と __init__ の比較

2022-05-11 13:10:58

質問

これから質問する内容は、重複しているようですが Pythonの__new__と__init__の使い分け? の実用的な違いは何なのか、私にはまだはっきりしません。 __new____init__

慌てて言う前に __new__ はオブジェクトを作成するためのものであり __init__ はオブジェクトを初期化するためのものであることをはっきりさせておきましょう。 それはわかった。 実際、この区別は私にとって非常に自然なことです。なぜなら、私はC++で 新しい配置 があり、同様にオブジェクトの割り当てを初期化から分離しています。

Python C API チュートリアル ではこのように説明されています。

新しいメンバは 作成(初期化とは違う)する オブジェクトの作成を担当します。これは Pythonでは __new__() メソッドとして公開されています。... 新しいメソッドを実装する理由のひとつは、インスタンス変数の初期値を確保するためです。 インスタンス変数 .

そうそう - 私 を得る __new__ が行っていることですが、それにもかかわらず、私は まだ は、なぜそれがPythonで有用なのか理解できません。 与えられた例では __new__ は、インスタンス変数の初期値を保証したい場合に便利です。まあ、それはまさに __init__ がそうするのでは?

C API チュートリアルでは、新しい Type ("Noddy") が作成され、その Type の __new__ 関数が定義されています。 Noddy 型には、以下のような文字列メンバがあります。 first という文字列メンバがあり、この文字列メンバはこのように空文字列で初期化されます。

static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    .....

    self->first = PyString_FromString("");
    if (self->first == NULL)
    {
       Py_DECREF(self);
       return NULL;
    }

    .....
}

ただし __new__ メソッドが定義されていない場合、私たちは PyType_GenericNew を使用する必要があります。これは単にインスタンス変数のメンバをすべて NULL に初期化するだけです。 つまり __new__ メソッドの唯一の利点は、インスタンス変数がNULLではなく空の文字列として始まることです。 しかし、なぜこれが役に立つのでしょうか。インスタンス変数がデフォルト値で初期化されていることを確認することに関心があるのなら、単に __init__ メソッドで行うことができたのに。

どのように解決するには?

この違いは主にmutable型とimmutable型で発生します。

__new__ タイプ を最初の引数として受け取り、(通常)その型の新しいインスタンスを返します。したがって、mutable型とimmutable型の両方での使用に適しています。

__init__ インスタンス を第一引数として受け取り、そのインスタンスの属性を変更します。これは不変型には不適切で、作成後に obj.__init__(*args) .

の振る舞いを比較してみましょう。 tuplelist :

>>> x = (1, 2)
>>> x
(1, 2)
>>> x.__init__([3, 4])
>>> x # tuple.__init__ does nothing
(1, 2)
>>> y = [1, 2]
>>> y
[1, 2]
>>> y.__init__([3, 4])
>>> y # list.__init__ reinitialises the object
[3, 4]

なぜ別々なのかというと(単純な歴史的理由は別として)。 __new__ メソッドは、正しく動作させるために多くの定型文を必要とします (最初のオブジェクトの作成、そして最後にオブジェクトを返すことを忘れないように)。 __init__ メソッドは、対照的に、設定する必要がある属性を設定するだけなので、非常にシンプルです。

はさておき __init__ メソッドを書くのが簡単であることや、前述の mutable と immutable の区別は別として、この分離を利用して、親クラスの __init__ で絶対必要なインスタンス不変量を設定することで、 サブクラスで親クラス を任意に呼び出せるようにすることもできます。 __new__ . しかし、これは一般的にはあまり良い方法ではありません。 __init__ メソッドを呼び出す方がより明確です。