641

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

ただし、なぜ__init__が常に after と呼ばれるのかについては、少し混乱してい__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

なんで?

4

18 に答える 18

672

__new__新しいインスタンスの作成を制御する必要がある場合に使用します。

__init__新しいインスタンスの初期化を制御する必要がある場合に使用 します。

__new__インスタンス作成の最初のステップです。最初に呼び出され、クラスの新しいインスタンスを返す責任があります。

対照的に、 __init__何も返しません。インスタンスが作成された後のインスタンスの初期化のみを担当します。

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

2008 年 4 月の投稿から:いつ使用するか__new__vs. __init__? mail.python.org で。

あなたがやろうとしていることは通常Factoryで行われ、それが最善の方法であると考える必要があります。使用__new__はきれいな解決策ではありませんので、工場での使用を検討してください。ここに良い例があります: ActiveState Fᴀᴄᴛᴏʀʏ ᴘᴀᴛᴛᴇʀɴ Recipe .

于 2009-03-23T17:23:04.923 に答える
190

__new__は静的クラスメソッドであり、__init__はインスタンスメソッドです。 __new__最初にインスタンスを作成する必要があるため、インスタンス__init__を初期化できます。__init__パラメータとして取ることに注意しselfてください。インスタンスを作成するまで、はありませんself

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

また、Python 2.6以降、クラスデコレータを使用できます。

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

@singleton
class MyClass:
  ...
于 2009-03-23T17:20:19.157 に答える
169

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

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

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

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

多くの場合、 のみが必要になるように、または が不要になるようにリファクタリングでき__new__ます。しかし、本当にやりたいのであれば、Python では実際に「ジョブ」を再定義できるので、必ずしも呼び出しの後に が続くとは限りません。これを行うには、メタクラスを作成し、そのメソッドを定義する必要があります。__new____init__SomeClass(arg1, arg2)__new____init____call__

メタクラスは単なるクラスのクラスです。クラスの__call__メソッドは、クラスのインスタンスを呼び出したときに何が起こるかを制御します。したがって、メタクラス__call__メソッドは、クラスを呼び出したときに何が起こるかを制御します。つまり、インスタンス作成メカニズムを最初から最後まで再定義できます。これは、シングルトン パターンなどの完全に非標準のインスタンス作成プロセスを最もエレガントに実装できるレベルです。実際、10 行未満のコードでメタクラスを実装することができますSingletonこのメタクラスは、.__new__ __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

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

于 2011-12-29T07:39:47.963 に答える
29

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

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

...

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

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

于 2009-03-23T17:29:23.317 に答える
13

この質問はかなり古いことを認識していますが、同様の問題がありました。以下は私が望んでいたことをしました:

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

于 2013-05-13T14:12:10.363 に答える
10

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

于 2013-07-07T14:45:17.557 に答える
7
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プロパティはAasから自動的にアクセスできるようになるA._dictため、誤って上書きしないように注意してください。

于 2015-07-30T12:04:51.010 に答える
5

そこをもう少し掘り下げます!

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

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

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

次にtype_call、変数にバインドするオブジェクトを返します。

于 2016-11-15T20:47:55.403 に答える
5

__new__ は、クラスの新しい空のインスタンスを返す必要があります。次に __init__ が呼び出され、そのインスタンスが初期化されます。__new__ の「NEW」ケースでは __init__ を呼び出していないため、呼び出されています。呼び出しているコードは__new__、特定のインスタンスで __init__ が呼び出されたかどうかを追跡しません。これは、ここで非常に珍しいことをしているためです。

__init__ 関数でオブジェクトに属性を追加して、初期化されたことを示すことができます。__init__ で最初にその属性の存在を確認し、存在する場合はそれ以上先に進まないでください。

于 2009-03-23T17:23:42.907 に答える
5

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

さて、Python の場合は__new__、ユーザーがアクセスできる追加機能です。Java は、その型付けされた性質のために、そのような柔軟性を提供しません。言語がその機能を提供する場合、 の実装者は__new__、場合によっては無関係なオブジェクトのまったく新しいインスタンスを作成するなど、インスタンスを返す前にそのメソッドで多くのことを行うことができます。また、このアプローチは、特に Python の場合の不変型に対してもうまく機能します。

于 2014-04-24T01:30:44.927 に答える
5

ただし、なぜ__init__が常に after と呼ばれるのかについては、少し混乱してい__new__ます。

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

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

  2. __init__オブジェクトの内部変数を特定の値に初期化します (デフォルトの可能性があります)。

于 2019-02-11T22:38:05.490 に答える
2

__init__後で呼び出される__new__ため、サブクラスでオーバーライドすると、追加されたコードが引き続き呼び出されます。

すでに を持っているクラスをサブクラス化しようとしている場合__new__、これに気付いていない誰かが、 を適応させ__init__て呼び出しをサブクラス に転送することから始める可能性があり__init__ます。__init__afterを呼び出すというこの規則は__new__、期待どおりに機能するのに役立ちます。

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

元の投稿者が説明した動作のため、同じレベルの継承で と の両方__new__を同じクラスに持つことは一般的に悪い形式です。__init__

于 2012-06-18T16:30:45.803 に答える
1

ただし、なぜ__init__が常に after と呼ばれるのかについては、少し混乱してい__new__ます。

それがそのように行われているということ以外の理由はあまりありません。__new__クラスを初期化する責任はありませんが、他のメソッドが責任を負います ( __call__、おそらく--確かにはわかりません)。

私はこれを期待していませんでした。なぜこれが起こっているのか、それ以外の方法でこの機能を実装する方法を教えてもらえますか? (実装を に入れることは別として、__new__かなりハッキーに感じます)。

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

于 2009-03-23T17:23:37.900 に答える
1

単純な理由は、インスタンスの作成にはnewが使用され、インスタンスの初期化にはinitが使用されるためです。初期化する前に、まずインスタンスを作成する必要があります。そのため、initの前にnewを呼び出す必要があります。

于 2018-03-23T13:48:43.893 に答える