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)
)?
Краткий ответ: нет, нет способа неявно вызвать правильный __ init __
с правильными аргументами правого родительского класса в Python 2 .Икс.
Между прочим, показанный здесь код неверен: если вы используете super (). __ init __
, тогда все классы в вашей иерархии должны иметь одинаковую сигнатуру в своих методах __ init __
. В противном случае ваш код может перестать работать, если вы введете новый подкласс, который использует множественное наследование.
См. http://fuhm.net/super-harmful/ для более подробного описания проблемы (с изображениями).
Возможно, то, что вы ищете это метаклассы?
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()
Выводит «Я класс A»
Метакласс отменяет первоначальное создание класса B (а не объекта) и проверяет, что встроенный словарь для каждого объекта B теперь содержит базовый массив где вы можете найти все базовые классы для B
Ваш код не имеет никакого отношения к порядку разрешения методов. Разрешение методов происходит в случае множественного наследования, что не относится к вашему примеру. Ваш код просто неверен, потому что вы предполагаете, что 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
prints:
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 использует метакласс для хранения атрибута, __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
>>>
Насколько мне известно, следующее обычно не выполняется. Но похоже, что это работает.
Методы в данном определении класса всегда искажают атрибуты с двойным подчеркиванием, чтобы включить имя класса, в котором они определены. Итак, если вы поместите ссылку на класс в измененной форме имени, где экземпляры могут ее видеть, вы можете использовать это при звонке на super
.
Пример размещения ссылок на самом объекте путем реализации __ new __
в базовом классе:
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
ссылки на сами объекты класса:
class ClassStasherType(type):
def __init__(cls, name, bases, attributes):
setattr(cls, mangle(cls, '__class'), cls)
class ClassStasher(object):
__metaclass__ = ClassStasherType
class A_meta(ClassStasher):
def __init__(self):
print 'init in A_meta', self.__class
super(self.__class, self).__init__()
class B_meta(A_meta):
def __init__(self):
print 'init in B_meta', self.__class
super(self.__class, self).__init__()
class C_meta(A_meta):
def __init__(self):
print 'init in C_meta', self.__class
super(self.__class, self).__init__()
class D_meta(B_meta, C_meta):
def __init__(self):
print 'init in D_meta', self.__class
super(self.__class, self).__init__()
d = D_meta()
print d
Выполнение всего этого вместе, как один исходный файл:
% 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>