web-dev-qa-db-ja.com

なぜ__init __()がいつも__new __()の後に呼び出されるのですか?

私は自分のクラスの1つを合理化しようとしているだけで、 flyweightデザインパターン と同じスタイルでいくつかの機能を導入しました。

しかし、なぜ__init__が常に__new__の後に呼び出されるのかについては少し混乱しています。私はこれを期待していませんでした。なぜこれが起こっているのか、そうでなければどうやってこの機能を実装できるのか、誰かに教えてもらえますか? (実装を__new__に入れることは別ですが、これはかなり厄介です。)

これが例です:

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()

出力:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

どうして?

498
Dan

新しいインスタンスの作成を制御する必要がある場合は、_ NEW _を使用してください。新しいインスタンスの初期化を制御する必要がある場合は、_ INIT _を使用してください。

_ NEW _は、インスタンス作成の最初のステップです。最初に呼び出され、クラスの新しいインスタンスを返す責任があります。対照的に、_ INIT _は何も返しません。インスタンスが作成された後に初期化するのは、それだけです。

一般的に、str、int、unicode、Tupleなどの不変の型をサブクラス化している場合を除き、_ NEW _をオーバーライドする必要はありません。

投稿者: http://mail.python.org/pipermail/tutor/2008-April/061426.html

あなたがやろうとしていることは通常 Factory で行われることを考慮する必要があります、そしてそれがそれをするための最良の方法です。 _ NEW _を使用するのは良い解決策ではないので、ファクトリーの使用法を検討してください。ここにあなたは 良い工場例 を持っています。

523
mpeterson

__new__は静的クラスメソッドですが、__init__はインスタンスメソッドです。 __new__は最初にインスタンスを作成しなければならないので、__init__はそれを初期化できます。 __init__はパラメータとしてselfを取ります。インスタンスを作成するまで、selfはありません。

さて、私はあなたがPythonで シングルトンパターン を実装しようとしていると集める。それにはいくつかの方法があります。

また、Python 2.6以降、class デコレータ を使うことができます。

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...
158
vartec

最もよく知られているOO言語では、SomeClass(arg1, arg2)のような式は新しいインスタンスを割り当て、インスタンスの属性を初期化してから返します。

最もよく知られているOO言語では、constructorを定義することにより、クラスごとに「インスタンスの属性を初期化する」部分をカスタマイズできます。 (コンストラクター式に提供された引数を使用して)新しいインスタンスで、必要な初期条件を設定します。 Pythonでは、これはクラスの__init__メソッドに対応します。

Pythonの__new__は、「新しいインスタンスを割り当てる」部分のクラスごとの同様のカスタマイズにほかなりません。もちろん、これにより、新しいインスタンスを割り当てるのではなく、既存のインスタンスを返すなどの異常なことを行うことができます。そのため、Pythonでは、この部分を必ずしも割り当てに関係していると考えるべきではありません。必要なのは、__new__がどこかから適切なインスタンスを作成することです。

しかし、それはまだ仕事の半分に過ぎず、Pythonシステムが、後で仕事の残りの半分(__init__)を実行したい場合と、そうしない場合があることを知る方法がありません。 t。その動作が必要な場合は、そのように明示的に言わなければなりません。

多くの場合、__new__のみを必要とするように、または__new__を必要としないように、または__init__が既に初期化されたオブジェクトに対して異なる動作をするようにリファクタリングできます。ただし、本当に必要な場合は、Pythonを使用して「ジョブ」を再定義できるため、SomeClass(arg1, arg2)__new__に続いて__init__を呼び出すとは限りません。これを行うには、メタクラスを作成し、__call__メソッドを定義する必要があります。

メタクラスはクラスのクラスです。また、クラスの__call__メソッドは、クラスのインスタンスを呼び出したときに何が起こるかを制御します。そのため、metaclass '__call__メソッドは、クラスを呼び出したときに何が起こるかを制御します。つまり、インスタンス作成メカニズムを最初から最後まで再定義するができます。これは、シングルトンパターンなどの完全に非標準のインスタンス作成プロセスを最もエレガントに実装できるレベルです。実際、10行未満のコードでSingletonメタクラスを実装できます。このメタクラスは、__new__at)を使用する必要さえなく、anyそれ以外の場合は、__metaclass__ = Singleton!を追加するだけで、シングルトンに通常のクラスが追加されます。

class Singleton(type):
    def __init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)
        self.__instance = None
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

ただし、これはおそらく、この状況で実際に保証されるよりも深い魔法です!

136
Ben

ドキュメントを引用するには

典型的な実装は、適切な引数で "super(currentclass、cls).__ new __(cls [、...])"を使用してスーパークラスの__new __()メソッドを呼び出し、必要に応じて新しく作成されたインスタンスを変更することでクラスの新しいインスタンスを作成します。それを返す前に。

...

__new __()がclsのインスタンスを返さない場合、新しいインスタンスの__init __()メソッドは呼び出されません。

__new __()は、主に不変型(int、str、Tupleなど)のサブクラスでインスタンス作成をカスタマイズできるようにすることを目的としています。

22
tgray

私はこの質問はかなり古いことを認識していますが、私は同様の問題を抱えていました。以下は私が欲しかったことをしました:

class Agent(object):
    _agents = dict()

    def __new__(cls, *p):
        number = p[0]
        if not number in cls._agents:
            cls._agents[number] = object.__new__(cls)
        return cls._agents[number]

    def __init__(self, number):
        self.number = number

    def __eq__(self, rhs):
        return self.number == rhs.number

Agent("a") is Agent("a") == True

このページをリソースとして使用しました http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html

11
Tobias

__new__が同じクラスのインスタンスを返すとき、__init__は後で返されたオブジェクトに対して実行されます。すなわち__new__が実行されないようにするために__init__を使用することはできません。 __new__から以前に作成したオブジェクトを返したとしても、__init__によって初期化されたdouble(tripleなど)になります。

これは上記のvartecの答えを拡張してそれを修正するシングルトンパターンへの一般的なアプローチです:

def SingletonClass(cls):
    class Single(cls):
        __doc__ = cls.__doc__
        _initialized = False
        _instance = None

        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super(Single, cls).__new__(cls, *args, **kwargs)
            return cls._instance

        def __init__(self, *args, **kwargs):
            if self._initialized:
                return
            super(Single, self).__init__(*args, **kwargs)
            self.__class__._initialized = True  # Its crucial to set this variable on the class!
    return Single

全文は ここ です。

実際に__new__を含む別のアプローチはclassmethodsを使うことです:

class Singleton(object):
    __initialized = False

    def __new__(cls, *args, **kwargs):
        if not cls.__initialized:
            cls.__init__(*args, **kwargs)
            cls.__initialized = True
        return cls


class MyClass(Singleton):
    @classmethod
    def __init__(cls, x, y):
        print "init is here"

    @classmethod
    def do(cls):
        print "doing stuff"

このアプローチでは、すべてのメソッドを@classmethodで装飾する必要があることに注意してください。実際のMyClassのインスタンスを使用することは決してないでしょう。

9
Zaar Hai

この質問に対する簡単な答えは、__new__がクラスと同じ型の値を返す場合、__init__関数が実行されるが、それ以外の場合は実行されないことです。この場合、コードはA._dict('key')を返します。これはclsと同じクラスです。そのため、__init__が実行されます。

9
PMN
class M(type):
    _dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print 'EXISTS'
            return cls._dict[key]
        else:
            print 'NEW'
            instance = super(M, cls).__call__(key)
            cls._dict[key] = instance
            return instance

class A(object):
    __metaclass__ = M

    def __init__(self, key):
        print 'INIT'
        self.key = key
        print

a1 = A('aaa')
a2 = A('bbb')
a3 = A('aaa')

出力:

NEW
INIT

NEW
INIT

EXISTS

注意副作用としてM._dictプロパティは自動的にAからA._dictとしてアクセス可能になるので、誤って上書きしないように注意してください。

6

__new__は、クラスの新しい空白のインスタンスを返します。そのインスタンスを初期化するために__init__が呼び出されます。 __new__の "NEW"の場合は__init__を呼び出していないので、それが呼び出されます。 __new__を呼び出しているコードは、__ init__が特定のインスタンスに対して呼び出されたかどうかを追跡しません。

__init__関数でオブジェクトに属性を追加して、それが初期化されたことを示すことができます。その属性が__init__の最初のものとして存在しているかどうかを確認してください。

5

@AntonyHatchkinsの回答を更新しました。おそらくメタタイプのクラスごとに個別のインスタンスのディクショナリが必要です。つまり、クラス全体をグローバルにするのではなく、そのディクショナリでクラスオブジェクトを初期化する__init__メソッドが必要です。

class MetaQuasiSingleton(type):
    def __init__(cls, name, bases, attibutes):
        cls._dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print('EXISTS')
            instance = cls._dict[key]
        else:
            print('NEW')
            instance = super().__call__(key)
            cls._dict[key] = instance
        return instance

class A(metaclass=MetaQuasiSingleton):
    def __init__(self, key):
        print 'INIT'
        self.key = key
        print()

私は先に進んで__init__メソッドを使って元のコードを更新し、構文をPython 3の表記法に変更しました(属性としてではなくクラス引数で引数なしのsuperとメタクラスを呼び出す)。

どちらにしても、ここで重要な点は、キーが見つかった場合にクラス初期化子(__call__メソッド)が__new__または__init__を実行しないことです。これは、デフォルトの__new__ステップをスキップしたい場合はオブジェクトをマークする必要がある__init__を使用するよりもはるかにクリーンです。

5
Mad Physicist

伝統的なOO言語では、__init__を単純なコンストラクタとして見てください。たとえば、JavaまたはC++に精通している場合は、コンストラクタにはそれ自体のインスタンスへのポインタが暗黙的に渡されます。 Javaの場合、それはthis変数です。 Java用に生成されたバイトコードを検査すると、2回の呼び出しに気付くでしょう。最初の呼び出しは "new"メソッドへの呼び出しであり、次に次の呼び出しはinitメソッドへの呼び出しです(これはユーザー定義コンストラクターへの実際の呼び出しです)。この2段階のプロセスにより、そのインスタンスの別のメソッドであるクラスのコンストラクターメソッドを呼び出す前に、実際のインスタンスを作成できます。

現在、Pythonの場合、__new__はユーザーがアクセスできる追加機能です。型付きの性質上、Javaはその柔軟性を提供しません。言語がその機能を提供している場合、__new__の実装者は、インスタンスを返す前に、そのメソッドで多くのことを行うことができます。そしてこのアプローチは、特にPythonの場合の不変型に対してもうまく機能します。

4
grdvnl

このドキュメントを参照すると

数字や文字列のような不変の組み込み型をサブクラス化するとき、そして時折他の状況では、静的メソッド new が役に立ちます。 new はインスタンス構築の最初のステップで、 init の前に呼び出されます。

new メソッドはクラスを最初の引数として呼び出されます。その責任はそのクラスの新しいインスタンスを返すことです。

これを init と比較してください。 init はインスタンスを最初の引数として呼び出され、何も返しません。その責任はインスタンスを初期化することです。

init を呼び出さずに新しいインスタンスが作成される場合があります(たとえば、インスタンスがpickleからロードされたときなど)。 new を呼び出さずに新しいインスタンスを作成する方法はありません(ただし、場合によっては基本クラスの new を呼び出すことで回避できます)。 。

あなたが達成したいことに関しては、シングルトンパターンについての同じdoc情報にもあります

class Singleton(object):
        def __new__(cls, *args, **kwds):
            it = cls.__dict__.get("__it__")
            if it is not None:
                return it
            cls.__it__ = it = object.__new__(cls)
            it.init(*args, **kwds)
            return it
        def init(self, *args, **kwds):
            pass

デコレータを使って、PEP 318からこの実装を使うこともできます。

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
...
4
kiriloff

もう少し掘り下げてみましょう!

CPythonのジェネリッククラスの型はtypeであり、その基本クラスはObjectです(メタクラスなどの別の基本クラスを明示的に定義しない限り)。低レベルの呼び出しのシーケンスは here にあります。最初に呼び出されるメソッドはtype_callで、次にtp_newを呼び出し、次にtp_initを呼び出します。

ここで興味深いのは、tp_newObjectの(基本クラス)新しいメソッドobject_newを呼び出して、メモリを割り当てるtp_allocPyType_GenericAlloc)を呼び出すことです。オブジェクト :)

その時点で、オブジェクトがメモリに作成され、__init__メソッドが呼び出されます。 __init__がクラスに実装されていない場合、object_initが呼び出され、何もしません:)

type_callは、変数にバインドするオブジェクトを返すだけです。

4
xpapazaf

__init____new__の後に呼び出されるため、サブクラスでオーバーライドしても、追加されたコードは呼び出されます。

すでに__new__を持つクラスをサブクラス化しようとしている場合、これに気付いていない誰かが__init__を適応させてサブクラス__init__に呼び出しを転送することから始めるかもしれません。 __init__の後に__new__を呼び出すというこの規則は、それが期待通りに機能するのを助けます。

__init__は、スーパークラス__new__が必要とするすべてのパラメータを依然として許可する必要がありますが、そうしないと、通常明確な実行時エラーが発生します。そして__new__はおそらく明示的に*argsと '** kw'を許すべきです。

元の投稿者が述べた振る舞いのため、__new____init__の両方を同じクラスの継承で同じクラスに持つのは一般的に悪い形式です。

2
Jay Obermark

しかし、なぜ__init__が常に__new__の後に呼び出されるのかについては少し混乱しています。

ここではC++の類推が役に立つと思います。

  1. __new__は単にオブジェクトにメモリを割り当てます。オブジェクトのインスタンス変数はそれを保持するためにメモリを必要とします、そしてこれは__new__ステップがすることです。

  2. __init__はオブジェクトの内部変数を特定の値に初期化します(デフォルトでも構いません)。

2
HAltos

しかし、なぜ__init__が常に__new__の後に呼び出されるのかについては少し混乱しています。

それが単にそのように行われるということ以外の理由はそれほど多くはありません。 __new__にはクラスを初期化する責任はありませんが、他の方法にはあります(__call__、おそらく - 私はよくわかりません)。

私はこれを期待していませんでした。なぜこれが起こっているのか、そうでなければどうやってこの機能を実装するのか、誰かに教えてもらえますか? (実装を__new__に入れることは別として、それはかなりハックな感じがします)。

すでに初期化されている場合は__init__に何もしないようにするか、新しいインスタンスで__call__を呼び出すだけの新しい__init__を使用して新しいメタクラスを作成し、それ以外の場合は__new__(...)を返すことができます。

1

今、私は同じ問題を抱えています、そしていくつかの理由で私はデコレータ、工場とメタクラスを避けることに決めました。私はこのようにしました:

メインファイル

def _alt(func):
    import functools
    @functools.wraps(func)
    def init(self, *p, **k):
        if hasattr(self, "parent_initialized"):
            return
        else:
            self.parent_initialized = True
            func(self, *p, **k)

    return init


class Parent:
    # Empty dictionary, shouldn't ever be filled with anything else
    parent_cache = {}

    def __new__(cls, n, *args, **kwargs):

        # Checks if object with this ID (n) has been created
        if n in cls.parent_cache:

            # It was, return it
            return cls.parent_cache[n]

        else:

            # Check if it was modified by this function
            if not hasattr(cls, "parent_modified"):
                # Add the attribute
                cls.parent_modified = True
                cls.parent_cache = {}

                # Apply it
                cls.__init__ = _alt(cls.__init__)

            # Get the instance
            obj = super().__new__(cls)

            # Push it to cache
            cls.parent_cache[n] = obj

            # Return it
            return obj

クラス例

class A(Parent):

    def __init__(self, n):
        print("A.__init__", n)


class B(Parent):

    def __init__(self, n):
        print("B.__init__", n)

使用中で

>>> A(1)
A.__init__ 1  # First A(1) initialized 
<__main__.A object at 0x000001A73A4A2E48>
>>> A(1)      # Returned previous A(1)
<__main__.A object at 0x000001A73A4A2E48>
>>> A(2)
A.__init__ 2  # First A(2) initialized
<__main__.A object at 0x000001A7395D9C88>
>>> B(2)
B.__init__ 2  # B class doesn't collide with A, thanks to separate cache
<__main__.B object at 0x000001A73951B080>
  • 警告:Parentを初期化しないでください。will他のクラスと衝突します - それぞれの子で別々のキャッシュを定義しない限り、それは違います。私たちが欲しいもの。
  • 警告:祖父母がおかしな振る舞いをするので、親を持つクラスのようです。 [未確認]

オンラインで試す

1
RedClover

単純な理由は、インスタンスの作成に new が使用され、インスタンスの初期化に init が使用されるためです。初期化する前に、インスタンスを最初に作成する必要があります。だからこそ new init の前に呼ばれるべきです。

1
ZQ Hu