2011-02-01 3 views
93

質問は約Python's use of __new__ and __init__?と重複しているようですが、実際には__new____init__の実際の違いは何であるかは不明です。Python(とPython C API):__new__と__init__

__new__はオブジェクトを作成するためのもので、__init__はオブジェクトを初期化するためのものです。私はそれを知っています。実際には、私はplacement newというC++での経験があり、同様にオブジェクトの割り当てを初期化から分離しているので、その区別は私にとっては自然なことです。

Python C API tutorialはこのようにそれを説明する:

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

だから、ええ - 私は何をするか__new__を取得が、それはPythonで便利ですなぜこれにもかかわらず、私はまだ理解していない。与えられた例では、__new__は "インスタンス変数の初期値を保証"したい場合に役に立ちます。さて、__init__はどういうことでしょうか?

C APIチュートリアルでは、新しいタイプ(「Noddy」と呼ばれる)が作成され、タイプの__new__関数が定義されている例が示されています。 Noddyクラスタイプはfirstと呼ばれる文字列のメンバーが含まれており、この文字列のメンバーはそうのように空の文字列に初期化されます。ここで定義され__new__方法なしに、我々は単に初期化PyType_GenericNewを使用する必要があるだろうということ

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

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

    ..... 
} 

注意すべてのインスタンス変数メンバをNULLに設定します。したがって、__new__メソッドの唯一の利点は、インスタンス変数がNULLではなく空の文字列として開始されることです。 しかし、インスタンス変数がデフォルト値に初期化されていることを気にすると、__init__メソッドでこれを実行できたのでしょうか?

答えて

105

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

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

__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__方法から

、および不変の区別対可変の分離も__new__に任意の絶対的に必要なインスタンスの不変条件を設定して、オプションのサブクラスで親クラス__init__を呼び出す作るために活用することができ、上記の。これは一般的には嫌なことですが、必要に応じて親クラスの__init__メソッドを呼び出すことは、通常は明らかです。

+1

ボイラープレートは決して変更されないので、 '__new__'で"ボイラープレート "と呼ぶコードはボイラープレートではありません。場合によっては、その特定のコードを別のものに置き換える必要があります。 –

+10

インスタンス(通常は 'super'コール)を作成するか、インスタンスを返すことは、' __new__'実装の必要な部分であり、私が参照している "定型句"です。対照的に、 'pass'は' __init__'のための有効な実装です - 必要な振る舞いはまったくありません。 – ncoghlan

24

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

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

これまでのところ、最もリーンな説明です。 – Tarik

+0

本当ではありません。私は 'self____ class__ = type(...)'のようなコードを含む '__init__'メソッドを持っています。そのため、オブジェクトは、作成していると思ったものとは異なるクラスになります。あなたがしたように実際にはint型に変更することはできません...ヒープ型や何かについてはエラーが発生しますが、動的に作成されたクラスに割り当てるという私の例が働きます。 – ArtOfWarfare

31

おそらく__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__でこれを行うことはできません - あなたは__init__selfを変更しようとした場合、インタプリタはあなたが不変オブジェクトを変更しようとしていることに文句を言うでしょう。

+1

スーパーを使用する理由は分かりません。つまり、__new__がスーパークラスのインスタンスを返すのはなぜですか?さらに、あなたが書いたように、どうして明示的に__new__にclを渡すべきですか? super(ModularTuple、cls)はバインドされたメソッドを返しませんか? – Alcott

+1

@Alcott、あなたは '__new__'の動作を誤解していると思います。 [ここ](http://docs.python.org/reference/datamodel.html#object.__new__) '__new__'を読むことができるので、_always_は最初の型として型を必要とするため、' cls'を '__new__'に明示的に渡します引数。次に、その型のインスタンスを返します。だから、スーパークラスのインスタンスを返さず、 'cls'のインスタンスを返しています。この場合、 'tuple .__ new __(ModularTuple、tup)'と同じことになります。 – senderle

11

完全な回答ではなく、その違いを示すものである可能性があります。

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

+0

メモリを割り当ててデータを初期化したければ、__inew__を__new__から呼び出すことはできますか?インスタンスを作成するときに__new__が存在しない場合__init__が呼び出されるのはなぜですか? –

+1

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

0

の意味を(動作とは対照的に)と定義して、__init__を定義したいだけです。

私はクラスファクトリを定義する最善の方法を理解しようとしていたときに、この質問に遭遇しました。

ので新しい方法の唯一の利点があることである:私は__new____init__から概念的に異なっている方法の1つは、__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 

これは単なる例示的な例です。上記のようなクラスファクトリのアプローチに頼らずにソリューションを得る方法はいくつかあります。このようにソリューションを実装することを選択したとしても、明示的にメタクラスを宣言するなどの注意点が少しありますあなたは()非メタクラス別名通常のクラスを作成している場合、それは、本質的に、より具体的な例であるncoghlan's answerの答え(中不変のシナリオに対して可変のような特殊なケースでない限り)

、そして__new__は本当に意味がありません。 __init__によって初期化される__new__によって生成されているクラス/タイプの初期値/プロパティを定義するというコンセプトのことです。

関連する問題