15

何らかの理由で定義されたクラス 'Parent' と、それ__new__を継承するクラス 'Child' があるとします。(私の場合、変更できないサードパーティのクラスから継承しようとしています)

class Parent:
    def __new__(cls, arg):
        # ... something important is done here with arg

私の試みは:

class Child(Parent):
    def __init__(self, myArg, argForSuperclass):
         Parent.__new__(argForSuperclass)
         self.field = myArg

しかし、

p = Parent("argForSuperclass")

期待どおりに動作します

c = Child("myArg", "argForSuperclass")

__new__'Child'は、独自のメソッドではなく、'Parent' から継承したメソッドを呼び出そうとするため、失敗します__init__

期待どおりの動作を得るには、「子」で何を変更する必要がありますか?

4

3 に答える 3

13

まず、__new__これらの問題を回避するためにオーバーライドすることはベストプラクティスとは見なされません...しかし、それはあなたのせいではありません。このような場合、オーバーライドのベスト プラクティス__new__は、オプションのパラメーターを受け入れるようにすることです...

class Parent(object):
    def __new__(cls, value, *args, **kwargs):
        print 'my value is', value
        return object.__new__(cls, *args, **kwargs)

...子供たちが自分のものを受け取ることができるように:

class Child(Parent):
    def __init__(self, for_parent, my_stuff):
        self.my_stuff = my_stuff

次に、それは機能します:

>>> c = Child(2, "Child name is Juju")
my value is 2
>>> c.my_stuff
'Child name is Juju'

ただし、親クラスの作成者はそれほど賢明ではなく、次の問題を引き起こしました。

class Parent(object):
    def __new__(cls, value):
        print 'my value is', value
        return object.__new__(cls)

この場合、__new__子をオーバーライドして、オプションのパラメーターを受け入れ、__new__そこにある親を呼び出します。

class Child(Parent):
    def __new__(cls, value, *args, **kwargs):
        return Parent.__new__(cls, value)
    def __init__(self, for_parent, my_stuff):
        self.my_stuff = my_stuff
于 2012-05-28T19:03:05.697 に答える
6

Child はそれ自体の__new__代わりにParent を呼び出さず、 の前に__init__それ自身の__new__(Parent から継承された) を呼び出します。 __init__

これらのメソッドの目的と、Python がそれらを呼び出すタイミングを理解する必要があります。__new__インスタンス オブジェクトを作成するために__init__呼び出され、そのインスタンスの属性を初期化するために呼び出されます。Python は両方のメソッドに同じ引数を渡します。プロセスを開始するためにクラスに渡される引数です。

したがって、クラスから継承するときに__new__行うことは、親に必要なシグネチャとは異なるシグネチャを持つ他のメソッドを継承するときに行うこととまったく同じです。__new__Child の引数を受け取るようにオーバーライドし、必要な引数で呼び出す必要がありますParent.__new__。次に、完全に個別にオーバーライドします__init__(ただし、同じ引数リストを__init__使用し、独自の予想される引数リストを使用して親を呼び出す必要があります)。

__new__ただし、新しいインスタンスを取得するプロセスをカスタマイズするため、2 つの潜在的な問題が発生する可能性があります。実際に新しいインスタンスを作成しなければならない理由はなく (既に存在するインスタンスを見つけることもできます)、実際、クラスのインスタンスを返す必要さえありません。これにより、次の問題が発生する可能性があります。

  1. 既存のオブジェクトを返す場合__new__(たとえば、Parent はそのインスタンスをキャッシュできると想定しているため)、__init__オブジェクトの状態が変更可能であると想定されている場合、これは非常に悪い可能性があります。しかし、Parent がこの種のことを実行できることを期待している場合、その使用方法に関する一連の制約が予想され、これらの制約を任意に破る方法でサブクラス化されます (たとえば、変更可能な状態をクラスに追加するなど)。インスタンスをキャッシュしてリサイクルできるように不変であることが期待されます) は、オブジェクトを正しく初期化できたとしても、ほぼ確実に機能しません。この種のことが起こっていて、Parent をサブクラス化するために何をすべきかを示すドキュメントがない場合、サードパーティはおそらくあなたが Parent をサブクラス化することを意図しておらず、事態は困難になるでしょう。あなたのために。__new__ではなく、__init__それは醜いです。

  2. Pythonは、クラスのメソッドが実際にクラスのインスタンスを返す場合にのみメソッドを呼び出します。親がそのメソッドをある種の「ファクトリ関数」として使用して他のクラスのオブジェクトを返す場合、単純な方法でのサブクラス化は失敗する可能性が非常に高くなります。__init____new____new__

于 2012-05-28T22:14:18.380 に答える
1

私が理解しているように、 を呼び出すChild("myarg", "otherarg")と、実際には次のような意味になります。

c = Child.__new__(Child, "myarg", "otherarg")
if isinstance(c, Child):
    c.__init__("myarg", "otherarg")

あなたは出来る:

  1. Child.create_with_extra_arg("myarg", "otherarg")のように、必要なことを行う前にインスタンス化する代替コンストラクターをChild("otherarg")記述します。

  2. Override Child.__new__、次のようなもの:

.

def __new__(cls, myarg, argforsuperclass):
    c = Parent.__new__(cls, argforsuperclass)
    c.field = myarg
    return c

私はそれをテストしていません。オーバーライド__new__はすぐに混乱を招く可能性があるため、可能であれば避けることをお勧めします。

于 2012-05-28T18:51:53.133 に答える