2016-03-30 19 views
4

私はやや奇妙なメタクラスの質問があります。私は別のスーパークラスから継承し、元のクラスの属性としてそれを割り当てる '兄弟'クラスを動的に作成するためにメタクラスを使用しています。以下は、最小限の設定です:pythonメタクラスの継承問題

class Meta(type): 
def __new__(cls, name, parents, dct): 
    sdct = dct.copy() 
    dct['sibling'] = type(name+'Sibling', (Mom,), sdct) 
    return super().__new__(cls, name, (Dad,), dct) 

class Mom: 
    def __init__(self): 
     self.x = 3 

class Dad: 
    def __init__(self): 
     self.x = 4 

class Child(metaclass=Meta): 
    def __init__(self): 
     super().__init__() 
     self.y = 1 # <<< added from feedback 
print(Child().x) # 4 
print(Child().sibling) # <class '__main__.Child'> | should be ChildSibling 
print(Child().sibling().x) # should be 3 instead throws: 
    # TypeError: super(type, obj): obj must be an instance or subtype of type 
print(Child().sibling().y) # should print 4 

何かが「兄弟」クラスの作成に上記の間違っているように見えるが、私は何をかなりよく分かりません。たとえば、これがうまくいくことがわかります:

class ChildAbstract: 
    def __init__(self): 
     super().__init__() 

ChildSibling = type('ChildSibling', (ChildAbstract, Mom), {}) 
Child = type('Child', (ChildAbstract, Dad), {'sibling': ChildSibling}) 
print(Child().sibling().x) # 3 

私は2つのケースの違いは分かりません。

答えて

5

タイプに渡される辞書SDCTこのPEPによればのreprSTRが現在使用するものである、__qualname__を含みます。

print(Child is Child.sibling) # False 
print(Child.sibling.__name__) # "ChildSibling" 

を追加してみてください、あなたはそれが本当に兄弟であることがわかります。 sibling().xがスロー理由として

は、非常に同じSDCTもすでに動的に作成された新しいタイプChildSibling__init__として終わるChild.__init__が含まれています。 sibling()へ通話中super()コールはChildにクラスを解決し、ChildSiblingのインスタンスが与えられる:

また、脇ゼロ引数フォーム、スーパー(からの)メソッド内での使用に限定されるものではありません。 2つの引数形式は、引数を正確に指定し、適切な参照を作成します。ゼロ引数フォームは、クラス定義内でのみ動作します。コンパイラは、定義されているクラスを正しく取得し、通常のメソッドの現在のインスタンスにアクセスするために必要な詳細を入力します。現在のインスタンスへのアクセス

https://docs.python.org/3/library/functions.html#super

インスタンスの方法の最初の引数を渡すことによって行われます。

super() -> same as super(__class__, <first argument>)

エラーがline 7210 of Object/typeobject.cで上昇しています。

はであなたの__new__で間違った__init__を削除してください:

del sdct['__init__'] 

となりまし

print(Child().sibling().x) 

3を印刷します。しかし、唯一の理由は、私のおもちゃの例Iドンで辞書作品から__init__削除、

def __init__(self): 
    super(self.__class__, self).__init__() 
+0

興味深いポイント:

ソリューションは、「汎用」の継承と__init__友好メタプログラミングはsuper()の2引数形式を使用することです子プロセスでは何もしないでください.____ init__これは問題をかなり解決していません(説明するために更新されたコード)。 – Buck

+0

ああ、上のノートではこれを説明しています – Buck

+0

@Buck 2つ目の例が動作する理由は、2つの動的タイプの基底に 'ChildAbstract'が含まれていて、' super() 'が満足しているということです。 –