私が尋ねる質問は、 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__
メソッドでそれを行うことができたので、なぜこれが便利なのでしょうか?
違いは、主に可変型と不変型で発生します。
___new__
_は、最初の引数としてtypeを受け入れ、(通常)そのタイプの新しいインスタンスを返します。したがって、可変型と不変型の両方での使用に適しています。
___init__
_は、最初の引数としてinstanceを受け入れ、そのインスタンスの属性を変更します。これは、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__
_メソッドを必要に応じて呼び出す方が通常は明確です。
おそらく__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
を変更しようとすると、インタープリターは不変オブジェクトを変更しようとしていると文句を言います。
__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__
)。
__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__
で初期化される概念のより具体的な例)。