2016-03-30 7 views
4

У меня есть несколько странный вопрос о метаклассе. Я использую метакласс для динамического создания класса «sibling», который наследуется от другого суперкласса и назначает его как атрибут исходного класса. Ниже приводится минимальная установка:проблема наследования метакласса 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 

Я не вижу разницы между этими двумя случаями.

ответ

5

Словарь sdct передается типа включает __qualname__, которые в соответствии с этим PEP что магнезии и ул теперь используют.

Попробуйте добавить

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

, и вы увидите, что это действительно родной брат.

Как почему sibling().x бросает, тот же sdct также уже содержит Child.__init__, который заканчивается в качестве __init__ вашего динамически созданного нового типа ChildSibling. Во время вызова sibling()super() вызова разрешающего класса Child и дается экземпляр ChildSibling:

отметить также, что в стороне от нулевой формы аргумента, супер() не ограничивается использованием внутри методов. Две формы аргумента точно определяют аргументы и делают соответствующие ссылки. Форма нулевого аргумента работает только внутри определения класса, , поскольку компилятор заполняет необходимые данные, чтобы правильно получить класс, определяемый, а также доступ к текущему экземпляру для обычных методов.

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

Доступ к текущему экземпляру осуществляется путем пропускания первого аргумента методу, как, например.

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

Ошибка возникает при line 7210 of Object/typeobject.c.

Попытка удаления неправильно __init__ в вашем __new__ с:

del sdct['__init__'] 

и теперь

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

напечатает 3.

Решение «родовое» наследование и мета-программирование дружелюбнее __init__ является использование 2 аргумента формы super():

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

Интересный момент, удаление __init__ из словаря работает, но только потому, что в моем примере игрушек я не ничего не делайте в Child .__ init__, так что это не совсем решает проблему (обновленный код для иллюстрации). Я посмотрю на __qualname__ хотя – Buck

+0

Ahh примечание о супер объясняет это – Buck

+0

@Buck Причина, по которой ваш второй пример работает, состоит в том, что основы этих двух динамических типов включают 'ChildAbstract', и поэтому' super() 'счастлив. –