2016-01-14 14 views
6

__new__機能を使用して、サブクラスの__init__機能にコードを注入することに興味があります。ドキュメントから私の理解は、__new__によって返されるインスタンス上の__init__を呼び出すということです。しかし、__init__の値を__new__から返す前のインスタンスに変更しようとする私の努力はうまくいかないようです。__new__を使用してサブクラスで__init__を上書きする

class Parent(object): 

    def __new__(cls, *args, **kwargs): 
     new_object = super(Parent, cls).__new__(cls) 
     user_init = new_object.__init__ 
     def __init__(self, *args, **kwargs): 
      print("New __init__ called") 
      user_init(self, *args, **kwargs) 
      self.extra() 
     print("Replacing __init__") 
     setattr(new_object, '__init__', __init__) 
     return new_object 

    def extra(self): 
     print("Extra called") 

class Child(Parent): 

    def __init__(self): 
     print("Original __init__ called") 
     super(Child, self).__init__() 

c = Child() 

上記のコードを印刷:

Replacing __init__ 
Original __init__ called 

が、私はそれが

Replacing __init__ 
New __init__ called 
Original __init__ called 
Extra called 

なぜを印刷するように期待?

私は、__init__の元の値を__new__に設定しているかどうかにかかわらず、Pythonが元の値を呼び出すように感じます。 c.__init__でイントロスペクションを実行すると、新しいバージョンが存在することが示されますが、オブジェクトの作成の一部として呼び出されていません。

+0

あなたの質問はありますか? – cat

答えて

0

__init__は特殊な関数であり、内部的にはクラスメソッドとして定義されているため、オブジェクトのインスタンスでそれを再割り当てすることはできません。

Pythonでは、すべてのオブジェクトはCのPyObjectで表され、PyTypeObjectへのポインタがあります。これは、私が__init__関数へのポインタを含むと信じているtp_initと呼ばれるメンバーを含んでいます。

他の解決策は、オブジェクトのインスタンスではなくクラスを変更するためです。

+0

'PyTypeObject'は' type'オブジェクトを表し、すべてのオブジェクトを表します、いいえ? –

+0

Argh。ソースコードでgithub経由で '' PyObject''を探していて、代わりに '' PyTypeObject''が見つかりました。 '' PyObject''は '' PyTypeObject''を指し示す '' ob_type''メソッドを持っていますので、私の推論はまだ立つことができます。これは本当に '' __init__''メソッドがどのように呼び出されるかによって異なります。 –

+0

私の答えを書き直して単純化しました。 –

1

まあ、__init__が呼び出される前に新しいオブジェクトが空になることが予想されます。だから、おそらくPythonは、最適化のように、オブジェクトに問い合わせるのを煩わせることなく、クラスから直接__init__を取得するようになります。

したがって、サブクラス自体の__init__を変更する必要があります。幸いにも、Pythonにはメタクラス用のツールがあります。 Pythonの2で

、あなたは特別なメンバーを設定することで、メタクラスを設定します。

class Parent(object): 
    __metaclass__ = Meta 
    ... 

は、Python 3ではPython2 documentation

を参照してください、あなたは親リストのキーワード属性でメタクラスを設定し、その

class Parent(metaclass=Meta): 
    ... 

Python3 documentation

私taclassはクラスインスタンスの基本クラスです。それはtypeから派生しなければなりません。それは__new__です。私は__init__も呼び出さなければならないと信じていますが、その例は__new__をオーバーライドしています。__new__は、あなたが持っているもののようになります。

class Meta(type): 
    def __new__(mcs, name, bases, namespace, **kwargs): 
     new_cls = super(Meta, mcs).__new__(mcs, name, bases, namespace, **kwargs) 
     user_init = new_cls.__init__ 
     def __init__(self, *args, **kwargs): 
      print("New __init__ called") 
      user_init(self, *args, **kwargs) 
      self.extra() 
     print("Replacing __init__") 
     setattr(new_cls, '__init__', __init__) 
     return new_cls 

(Pythonの3例を使用しますが、Pythonの2の署名が何**kwargsが存在しない以外は同様のようですが、それらを傷つけるべきではありません加える。Iそれをテストしなかった)。

関連する問題