Ошибки типа, использующие метаклассы в сочетании с множественным наследованием - PullRequest
3 голосов
/ 05 февраля 2010

У меня есть два вопроса, конвертирующие метаклассы и множественное наследование. Первый: почему я получаю TypeError для класса Derived, а не для Derived2?

class Metaclass(type): pass

class Klass(object):
    __metaclass__  = Metaclass

#class Derived(object, Klass): pass # if I uncomment this, I get a TypeError

class OtherClass(object): pass

class Derived2(OtherClass, Klass): pass # I do not get a TypeError for this

Точное сообщение об ошибке:

TypeError: Error when calling the metaclass bases Cannot create a consistent method resolution order (MRO) for bases object, Klass

Второй вопрос: почему super не работает в этом случае (если я использую __init__ вместо __new__, super снова работает):

class Metaclass(type):
    def __new__(self, name, bases, dict_):
        return super(Metaclass, self).__new__(name, bases, dict_)

class Klass(object):
    __metaclass__  = Metaclass

Там я получаю:

TypeError: Error when calling the metaclass bases type.__new__(X): X is not a type object (str)

Я использую Python 2.6.

Ответы [ 4 ]

7 голосов
/ 05 февраля 2010

На второй вопрос уже дважды был получен хороший ответ, хотя __new__ на самом деле является статическим, а не классовым методом, как ошибочно заявлено в комментарии ...:

>>> class sic(object):
...   def __new__(cls, *x): return object.__new__(cls, *x)
... 
>>> type(sic.__dict__['__new__'])
<type 'staticmethod'>

Первый вопрос (как кто-то заметил) не имеет ничего общего с метаклассами: вы просто не можете умножить наследование от любых двух классов A и B в таком порядке, где B является подклассом A. E.g.:

>>> class cis(sic): pass
... 
>>> class oops(sic, cis): pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases sic, cis

MRO гарантирует, что самые левые базы посещаются раньше, чем самые правые, но это также гарантирует, что среди предков, если x является подклассом y, то x посещается до y. В этом случае невозможно выполнить обе эти гарантии. Конечно, есть веская причина для таких гарантий: без них (например, в классах старого стиля, которые гарантируют только порядок слева направо в разрешении метода, не ограничение подкласса) все переопределения в x будут игнорироваться в пользу определения в у, и это не может иметь большого смысла. Подумайте об этом: что означает , что значит наследовать от object первого, а от какого-то другого класса - второго? Это определение object (по существу несуществующее ;-) его нескольких специальных методов должно иметь приоритет над другим классом, в результате чего переопределения другого класса игнорируются?

3 голосов
/ 05 февраля 2010

Для первого вопроса посмотрите описание MRO в python - в частности, раздел «Порядок разрешения неверных методов». По сути, это связано с тем, что python не знает, использовать ли объект или методы Klass. (Это не имеет ничего общего с использованием метаклассов.)

По второму вопросу похоже, что вы неправильно понимаете, как работает функция __new__. Он не принимает ссылку на себя в качестве первого аргумента - он принимает ссылку на тип создаваемого класса. Итак, ваш код должен выглядеть так:

class Metaclass(type):
    def __new__(cls, name, bases, dictn):
        return type.__new__(cls, name, bases, dictn)
0 голосов
/ 05 февраля 2010

Зачем вы это делаете?

class Derived(object, Klass):

Класс уже происходит от объекта.

class Derived(Klass):

Разумно ли здесь.

0 голосов
/ 05 февраля 2010

Для второго вопроса вам нужно передать себя в __new__, например:

class Metaclass(type):
    def __new__(self, name, bases, dict_):
        return super(Metaclass, self).__new__(self, name, bases, dict_)

class Klass(object):
    __metaclass__  = Metaclass

Я не могу вспомнить, почему это так, но я думаю, что это потому, что type.__new__ не является связанным методом и, таким образом, волшебным образом не получает аргумент self.

...