web-dev-qa-db-ja.com

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

私が尋ねる質問は、 Pythonの__new__と__init __? の使用と重複しているようですが、それにもかかわらず、__new____init__の実際の違いが何であるかはまだはっきりしていません。

__new__はオブジェクトの作成用であり、__init__はオブジェクトの初期化用であるとラッシュする前に、明確にしておきます: I get that.実際、その区別は私にとって非常に自然です。私はC++の経験があり、 placement new があります。これは、同様にオブジェクトの割り当てを初期化から分離します。

Python C APIチュートリアル は、次のように説明しています。

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

だから、そう-私はget__new__が何をするのか、しかしそれにもかかわらず、私はstillがなぜ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__メソッドを定義しないと、すべてのインスタンス変数メンバーをNULLに初期化するPyType_GenericNewを使用する必要があることに注意してください。したがって、__new__メソッドの唯一の利点は、インスタンス変数がNULLではなく空の文字列として開始されることです。 しかし、インスタンス変数がデフォルト値に初期化されていることを確認したい場合、__init__メソッドでそれを行うことができたので、なぜこれが便利なのでしょうか?

116
Channel72

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

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

___init___は、最初の引数としてinstanceを受け入れ、そのインスタンスの属性を変更します。これは、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___メソッドの記述が簡単であり、上記の可変対不変の区別は別として、分離は、絶対に必要なインスタンス不変式を設定することにより、サブクラスの親クラス___init___の呼び出しをオプションにするために利用することもできます___new___で。ただし、これは一般的に疑わしい習慣です。通常、親クラス___init___メソッドを必要に応じて呼び出す方が通常は明確です。

130
ncoghlan

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

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__でこれを行うことはできません-__init__selfを変更しようとすると、インタープリターは不変オブジェクトを変更しようとしていると文句を言います。

35
senderle

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

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

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

__new__は、オブジェクトを作成する必要があるときに常に呼び出されます。 __init__は呼び出されません。 1つの例として、ピクルファイルからオブジェクトのピクルを外すと、それらが割り当てられます(__new__)初期化されていません(__init__)。

11
Noufal Ibrahim

__new____init__を定義する(動作ではなく)intentについてWordを追加したいだけです。

クラスファクトリを定義するための最良の方法を理解しようとしたときに、(特に)この質問に出会いました。 __new__が概念的に__init__と異なる方法の1つは、__new__の利点が質問で述べられたとおりであることであることに気付きました。

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

記載されたシナリオを考慮すると、instanceが実際にはクラス自体である場合、インスタンス変数の初期値が重要です。したがって、実行時にクラスオブジェクトを動的に作成し、作成されるこのクラスの後続のインスタンスについて特別な何かを定義/制御する必要がある場合、これらの条件/プロパティをメタクラスの__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's answer answer(本質的には__new__で作成されるクラス/タイプの初期値/プロパティを定義してから__init__で初期化される概念のより具体的な例)。

2
lonetwin