7
class A(object): 
    def __init__(self, a, b, c): 
     #super(A, self).__init__() 
     super(self.__class__, self).__init__() 


class B(A): 
    def __init__(self, b, c): 
     print super(B, self) 
     print super(self.__class__, self) 
     #super(B, self).__init__(1, b, c) 
     super(self.__class__, self).__init__(1, b, c) 

class C(B): 
    def __init__(self, c): 
     #super(C, self).__init__(2, c) 
     super(self.__class__, self).__init__(2, c) 
C(3) 

В приведенной выше коде, закомментировано __init__ вызовов отображаются на быть общепринятым «умным» способом сделать супер инициализацию класса. Однако в том случае, если иерархия классов, вероятно, изменится, я до недавнего времени использовал форму без объявления.Косвенно вызов родительского класса инициализатору

Оказывается, что при вызове супер конструктор B в приведенном выше иерархии, что B.__init__ вызывается снова, self.__class__ на самом деле C, не B как я всегда считал.

Есть ли какой-то способ в Python-2.x, что я могу поддерживать надлежащее MRO (по отношению к инициализации всех родительских классов в правильном порядке) при вызове супер конструкторами, а не называя текущего класса (B в в super(B, self).__init__(1, b, c))?

ответ

3

Короткий ответ: нет, нет никакого способа, чтобы неявно ссылаться на правильный __init__ с правильными аргументами правого родительского класса в Python 2.x.

Кстати, код, показанный здесь, неверен: если вы используете super(). __init__, то все классы в вашей иерархии должны иметь одну и ту же подпись в своих методах __init__. В противном случае ваш код может перестать работать, если вы вводите новый подкласс, который использует множественное наследование.

См. http://fuhm.net/super-harmful/ для более подробного описания проблемы (с фотографиями).

+0

Я начал задумываться о подписях инициализации и множественном наследовании –

1

Возможно, что-то, что вы ищете - это метаклассы?

class metawrap(type): 
    def __new__(mcs,name, bases, dict): 
     dict['bases'] = bases 
     return type.__new__(mcs,name,bases,dict) 

class A(object): 
    def __init__(self): 
     pass 
    def test(self): 
     print "I am class A" 

class B(A): 
    __metaclass__ = metawrap 
    def __init__(self): 
     pass 
    def test(self): 
     par = super(self.bases[0],self) 
     par.__thisclass__.test(self) 
foo = B() 
foo.test() 

Печать «Я класс А»

Что метакласс делает, перекрывая начальное создание класса B (а не объект) и убеждается, что встроенный словарь для каждого объекта В настоящее время содержит в база баз, где вы можете найти все базовые очки для B

+0

Я только что понял, что стреляю воробьи с пушками (датская пословица), потому что я извиняюсь, но, тем не менее, пусть это будет, если кто-то не решит его удалить. –

+0

Я бы предпочел не прибегать к метаклассам для чего-то, что должно быть тривиально, но спасибо –

1

Ваш код не имеет никакого отношения к порядку разрешения метода. Разрешение метода происходит в случае множественного наследования, которое не относится к вашему примеру. Ваш код просто неправильно, потому что вы предполагаете, что self.__class__ на самом деле тот же класс один, где определен метод, и это неправильно:

>>> class A(object): 
...  def __init__(self): 
...   print self.__class__ 
... 
>>> 
>>> class B(A): 
...  def __init__(self): 
...   A.__init__(self) 
... 
>>> B() 
<class '__main__.B'> 
<__main__.B object at 0x1bcfed0> 
>>> A() 
<class '__main__.A'> 
<__main__.A object at 0x1bcff90> 
>>> 

поэтому, когда вы должны позвонить:

super(B, self).__init__(1, b, c) 

вы на самом деле вызова:

# super(self.__class__, self).__init__(1, b, c) 
super(C, self).__init__(1, b, c) 

EDIT: пытаясь лучше ответить на этот вопрос.

class A(object): 
    def __init__(self, a): 
     for cls in self.__class__.mro(): 
      if cls is not object: 
       cls._init(self, a) 
    def _init(self, a): 
     print 'A._init' 
     self.a = a 

class B(A): 
    def _init(self, a): 
     print 'B._init' 

class C(A): 
    def _init(self, a): 
     print 'C._init' 

class D(B, C): 
    def _init(self, a): 
     print 'D._init' 


d = D(3) 
print d.a 

печатает:

D._init 
B._init 
C._init 
A._init 
3 

(модифицированная версия template pattern).

Теперь методы родителей действительно называются неявно, но я должен согласиться с python zen, где явное лучше, чем неявное, потому что код менее читабельен, а коэффициент выигрыша плохой. Но будьте осторожны, что все методы _init имеют одинаковые параметры, вы не можете полностью забыть о родителях, и я не предлагаю этого.

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

Хорошо читает являются: how-does-pythons-super-do-the-right-thing и ссылка предложила в этом вопросе и в конкретности Python's Super is nifty, but you can't use it

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

EDIT 2

Другой пример меня в виду, но который использует метаклассы. Библиотека Urwid uses metaclass, чтобы сохранить атрибут, __super, в классе, так что вам нужно просто получить доступ к этому атрибуту.

Ex:

>>> class MetaSuper(type): 
...  """adding .__super""" 
...  def __init__(cls, name, bases, d): 
...   super(MetaSuper, cls).__init__(name, bases, d) 
...   if hasattr(cls, "_%s__super" % name): 
...    raise AttributeError, "Class has same name as one of its super classes" 
...   setattr(cls, "_%s__super" % name, super(cls)) 
... 
>>> class A: 
... __metaclass__ = MetaSuper 
... def __init__(self, a): 
... self.a = a 
... print 'A.__init__' 
... 
>>> class B(A): 
... def __init__(self, a): 
... print 'B.__init__' 
... self.__super.__init__(a) 
... 
>>> b = B(42) 
B.__init__ 
A.__init__ 
>>> b.a 
42 
>>> 
0

Насколько я знаю, обычно не делается следующее. Но, похоже, он работает.

Методы определения данного класса всегда используют атрибуты double-underscore для включения имени класса, в котором они определены. Таким образом, если вы закрепили ссылку на класс в обработанной форме, где экземпляры могут видеть это , вы можете использовать это при вызове super.

Пример припрятать ссылки на сам объект, путем реализации __new__ на BaseClass:

def mangle(cls, name): 
    if not name.startswith('__'): 
     raise ValueError('name must start with double underscore') 
    return '_%s%s' % (cls.__name__, name) 

class ClassStasher(object): 
    def __new__(cls, *args, **kwargs): 
     obj = object.__new__(cls) 
     for c in cls.mro(): 
      setattr(obj, mangle(c, '__class'), c) 
     return obj 

class A(ClassStasher): 
    def __init__(self): 
     print 'init in A', self.__class 
     super(self.__class, self).__init__() 

class B(A): 
    def __init__(self): 
     print 'init in B', self.__class 
     super(self.__class, self).__init__() 

class C(A): 
    def __init__(self): 
     print 'init in C', self.__class 
     super(self.__class, self).__init__() 

class D(B, C): 
    def __init__(self): 
     print 'init in D', self.__class 
     super(self.__class, self).__init__() 


d = D()  
print d 

И, делая подобную вещь, но с использованием мета-класса и припрятать в __class ссылки на объекты класса сами:

Запуск этого все вместе, так как один исходный файл:

% python /tmp/junk.py 
init in D <class '__main__.D'> 
init in B <class '__main__.B'> 
init in C <class '__main__.C'> 
init in A <class '__main__.A'> 
<__main__.D object at 0x1004a4a50> 
init in D_meta <class '__main__.D_meta'> 
init in B_meta <class '__main__.B_meta'> 
init in C_meta <class '__main__.C_meta'> 
init in A_meta <class '__main__.A_meta'> 
<__main__.D_meta object at 0x1004a4bd0>