Python(およびPython C API):__new__と__init__


126

私が尋ねようとしている質問は、Pythonの__new__と__init__の使用の複製のようです?しかしかかわらず、それは私にはまだ間、まさに実用的な違いは不明だ__new____init__あります。

__new__オブジェクトを作成するためのもので__init__あり、オブジェクトを初期化するためのものであると急いで説明する前に、はっきりさせておきます 実際、C ++で配置newがあり、オブジェクトの割り当てと初期化を分離している経験があるので、その区別は私にとって非常に自然です。

PythonのC APIのチュートリアルでは、このようにそれを説明します:

新しいメンバーは、タイプのオブジェクトを(初期化するのではなく)作成します。__new__()メソッドとしてPythonで公開されています。... 新しいメソッドを実装する1つの理由は、インスタンス変数の初期値を保証することです

だから、ええ-私は得るものを__new__行いますが、これにもかかわらず、私はまだそれがPythonで便利だ理由を理解していません。与えられた例は、__new__「インスタンス変数の初期値を保証する」場合に役立つ可能性があることを示しています。さて、それはまさに何を__init__するのでしょうか?

C APIチュートリアルでは、新しいタイプ(「Noddy」と呼ばれる)が作成され、タイプの__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__メソッドでそれを実行できたでしょうか。

回答:


137

違いは主に、可変型と不変型で発生します。

__new__最初の引数としてを受け入れ、(通常は)その型の新しいインスタンスを返します。したがって、可変型と不変型の両方での使用に適しています。

__init__最初の引数としてインスタンスを受け入れ、そのインスタンスの属性を変更します。これは、を呼び出して作成後に変更できるため、不変タイプには不適切ですobj.__init__(*args)

動作を比較tupleしてlist

>>> 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__書き込みが容易、かつ不変の区別は上記の対可変された方法は、分離は、親クラスを呼び出す作るために活用することができ__init__、任意の絶対に必要なインスタンスの不変条件を設定して、オプションのサブクラスで__new__。ただし、これは一般的に疑わしい慣行です。通常__init__、必要に応じて親クラスのメソッドを呼び出すだけの方が明確です。


1
__new__ボイラープレートは変更されないため、「ボイラープレート」として参照しているコードはボイラープレートではありません。特定のコードを別のコードに置き換える必要がある場合があります。
Miles Rout 2013年

13
(通常はsuper呼び出しを使用して)インスタンスを作成または取得し、インスタンスを返すことは、__new__実装の必要な部分であり、私が言及している「ボイラープレート」です。対照的に、passはの有効な実装です__init__。必要な動作はまったくありません。
ncoghlan 2013年

37

おそらく他の使用法もありますが、__new__本当に明白な使用法が1つあります__new__。を使用せずに不変型をサブクラス化することはできません。たとえば、0からまでの整数値のみを含むことができるタプルのサブクラスを作成したいとしますsize

class ModularTuple(tuple):
    def __new__(cls, tup, size=100):
        tup = (int(x) % size for x in tup)
        return super(ModularTuple, cls).__new__(cls, tup)

でこれを行うことはできません__init__-で変更しようとするself__init__、インタープリターは不変オブジェクトを変更しようとしていると不平を言うでしょう。


1
なぜsuperを使うべきなのかわかりません。newがスーパークラスのインスタンスを返す必要があるのはなぜですか?さらに、あなたの言うとおり、なぜclsを明示的にnewに渡さなければならないのですか?super(ModularTuple、cls)はバインドされたメソッドを返さないのですか?
Alcott

3
@Alcott、私はあなたがの動作を誤解していると思います__new__ここを読むことができるので、最初の引数として常に型が必要cls__new__ので、明示的に渡します。次に、そのタイプのインスタンスを返します。したがって、スーパークラスのインスタンスを返すのではなく、のインスタンスを返します。この場合、私たちが言ったのと同じです。 __new__ clstuple.__new__(ModularTuple, tup)
センダーレ、

35

__new__()バインドされているクラス以外のタイプのオブジェクトを返すことができます。__init__()クラスの既存のインスタンスのみを初期化します。

>>> class C(object):
...   def __new__(cls):
...     return 5
...
>>> c = C()
>>> print type(c)
<type 'int'>
>>> print c
5

これは、これまでで最も無駄のない説明です。
Tarik

まったくそうではありません。の__init__ようなコードを含むメソッドがありますself.__class__ = type(...)。これにより、オブジェクトは、作成していると思ったクラスとは異なるクラスになります。実際にintあなたがしたようにそれをに変更することはできません...ヒープタイプまたは何かについてエラーが発生します...しかし、動的に作成されたクラスに割り当てる例は機能します。
ArtOfWarfare 2016年

私も、いつ__init__()呼び出されるかについて混乱しています。たとえば、lonetwinの回答では、返されるタイプに応じて、Triangle.__init__()またはSquare.__init__()自動的に呼び出され__new__()ます。あなたがあなたの答えで言ったこと(そして私はこれを他の場所で読んだ)から、それがのインスタンス(またはそのサブクラスの1つ)を返さShape.__new__() ないので、どちらもそうあるべきではないようですcls
martineau

1
@martineau:__init__()lonetwinの回答のメソッドは、個々のオブジェクトがインスタンス化されるとき(つまり __new__()メソッドが戻るとき)に呼び出されShape.__new__()ます。
Ignacio Vazquez-Abrams

ああ、Shape.__init__()そうです(もしあれば)呼び出されません。今ではすべてが理にかなっています...:¬)
martineau

13

完全な答えではなく、おそらく違いを説明するものです。

__new__オブジェクトを作成する必要がある場合は常に呼び出されます。__init__呼び出されない状況がいくつかあります。一例として、pickleファイルからオブジェクトをunpickleすると、オブジェクトは割り当てられますが(__new__)、初期化されません(__init__)。


メモリを割り当ててデータを初期化したい場合、newからinitを呼び出しますか?インスタンスを作成するときにnewが存在しない場合、なぜinitが呼び出されますか?
redpix_

2
__new__メソッドの仕事は、クラスのインスタンスを作成し(これはメモリ割り当てを意味します)、それを返すことです。初期化は別のステップであり、通常はユーザーに表示されます。直面している特定の問題がある場合は、別の質問をしてください。
Noufal Ibrahim 2015

3

定義するかどうかの意図(動作ではなく)についての単語を追加したいだけです__new____init__

クラスファクトリーを定義する最良の方法を理解しようとしていたときに、この質問に(とりわけ)遭遇しました。__new__概念的に異なる点の1つは、__init__その利点が__new__質問で述べたとおりであることです。

したがって、__ new__メソッドの唯一の利点は、インスタンス変数がNULLではなく空の文字列として開始されることです。しかし、なぜこれが便利なのでしょう。インスタンス変数がデフォルト値に初期化されていることを気にしていたのであれば、__ init__メソッドでそれを実行できただけでしょうか?

前述のシナリオを考慮して、インスタンスが実際にはクラス自体である場合、インスタンス変数の初期値に注意します。したがって、実行時にクラスオブジェクトを動的に作成していて、作成されるこのクラスの後続のインスタンスについて何か特別なものを定義/制御する必要がある場合、これらの条件/プロパティを__new__メタクラスのメソッドでします。

コンセプトの意味だけではなく、コンセプトの適用について実際に考えるまで、私はこれについて混乱していました。うまくいけば違いが明確になる例を次に示します。

a = Shape(sides=3, base=2, height=12)
b = Shape(sides=4, length=2)
print(a.area())
print(b.area())

# I want `a` and `b` to be an instances of either of 'Square' or 'Triangle'
# depending on number of sides and also the `.area()` method to do the right
# thing. How do I do that without creating a Shape class with all the
# methods having a bunch of `if`s ? Here is one possibility

class Shape:
    def __new__(cls, sides, *args, **kwargs):
        if sides == 3:
            return Triangle(*args, **kwargs)
        else:
            return Square(*args, **kwargs)

class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return (self.base * self.height) / 2

class Square:
    def __init__(self, length):
        self.length = length

    def area(self):
        return self.length*self.length

これはあくまでも一例です。上記のようなクラスファクトリアプローチに頼らずにソリューションを取得する方法は複数あり、この方法でソリューションを実装することを選択した場合でも、簡潔にするために、いくつかの警告が省略されています(たとえば、メタクラスを明示的に宣言します) )

通常のクラス(別名非メタクラス)を作成している__new__場合、ncoghlanの回答の可変シナリオと不変シナリオのような特別なケースでない限り、実際には意味がありません。(これは基本的に定義の概念のより具体的な例です)介して作成されるクラス/タイプの初期値/特性__new__その後介して初期化します__init__)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.