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) 

W powyższym kodzie wykomentowane __init__ rozmowy pojawiają się do być powszechnie przyjętym „inteligentny” sposób Czy Super inicjalizacji klasy. Jednak w przypadku, gdy prawdopodobnie zmieni się hierarchia klas, do niedawna używam tej nieskomentowanej formy.Pośrednio powołując nadrzędnej klasy inicjator

Wydaje się, że w zaproszeniu do super konstruktora B w powyższej hierarchii, że B.__init__ nazywa znowu self.__class__ jest rzeczywiście C nie B jak ja zawsze zakładano.

Czy jest jakiś sposób w Pythonie 2.x, że mogę utrzymać właściwą MRO (w odniesieniu do inicjowania wszystkich klas dominujących we właściwej kolejności) podczas wywoływania Super konstruktorów jednocześnie nie nazywając obecną klasę (The B ww super(B, self).__init__(1, b, c))?

Odpowiedz

3

Krótka odpowiedź: nie, nie ma mowy, aby pośrednio wywołać odpowiednią __init__ z odpowiednimi argumentami właściwej klasy nadrzędnej w Pythonie 2.x.

Nawiasem mówiąc, kod wyświetlany tutaj jest niepoprawny: jeśli używasz super(). __init__, wówczas wszystkie klasy w hierarchii muszą mieć ten sam podpis w swoich metodach __init__. W przeciwnym razie Twój kod może przestać działać, jeśli wprowadzisz nową podklasę, która korzysta z dziedziczenia wielokrotnego.

Aby uzyskać dokładniejszy opis problemu (ze zdjęciami), patrz: .

+0

zacząłem zastanawiać się nad sygnaturami init i dziedziczeniem wielokrotnym –

1

Być może tym, czego szukasz, są metaklasy?

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() 

Drukuje „Jestem klasa A”

Co robi metaklasa jest nadrzędne początkową utworzenie klasy B (a nie obiektu) i zapewnia, że ​​wbudowany słownik dla każdego obiektu B zawiera teraz tablica bazowa, w której można znaleźć wszystkie klasy bazowe dla B

+0

Właśnie zdałem sobie sprawę, że strzelam wróble z armatami (przysłowie duńskie), za to przepraszam, jednak pozwolę na to, chyba że ktoś zdecyduje się usunąć. –

+0

Wolałbym nie uciekać się do metaclasses za coś, co powinno być trywialne, ale dzięki –

1

Twój kod nie ma nic wspólnego z kolejnością rozstrzygania metod. Rozdzielczość metody występuje w przypadku dziedziczenia wielokrotnego, co nie ma miejsca w przypadku twojego przykładu. Kod jest po prostu błędne, ponieważ zakładamy, że self.__class__ jest właściwie taka sama klasa to, w którym metoda jest zdefiniowana, a to jest złe:

>>> 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> 
>>> 

więc kiedy należy zadzwonić:

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

jesteś rzeczywiście dzwoni:

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

EDIT: próbując lepiej odpowiedzieć na pytanie.

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 

drukuje:

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

(zmodyfikowana wersja template pattern).

Metody rodziców są tak naprawdę nazywane niejawnie, ale muszę zgodzić się z python zen, gdzie jawność jest lepsza niż domniemana, ponieważ kod jest mniej czytelny, a zysk jest słaby. Ale pamiętaj, że wszystkie metody mają takie same parametry, nie możesz całkowicie zapomnieć o rodzicach i nie sugeruję tego robić.

W przypadku dziedziczenia pojedynczego lepszym podejściem jest jawne wywoływanie metody rodzica, bez wywoływania super. Czyniąc to, nie musisz nazwać aktualną klasę, ale nadal musisz zadbać o to, kto jest klasą rodzica.

Dobry czyta to: how-does-pythons-super-do-the-right-thing i linki sugerowane w tym pytaniu i szczegółowo Python's Super is nifty, but you can't use it

Jeśli hierarchia jest prawdopodobne, aby zmienić to objawy złej konstrukcji i ma konsekwencje we wszystkich częściach, którzy używają tego kodu i nie powinny być zachęconym.

EDIT 2

Innym przykładem przychodzi mi na myśl, lecz który wykorzystuje metaclasses. Biblioteka Urwid uses metaclass do przechowywania atrybutu, __super, w klasie, dzięki czemu potrzebujesz tylko dostępu do tego atrybutu.

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

Zgodnie z moją wiedzą poniższe nie jest powszechnie wykonywane. Ale wydaje się, że działa.

Metody w danej definicji klasy zawsze łączą się w podwójne atrybuty podkreślenia, aby uwzględnić nazwę klasy, w której są zdefiniowane. Tak więc, jeśli przechowujesz odwołanie do klasy w formie z nazwą, w której instancje mogą ją zobaczyć , możesz użyć tego w wywołaniu do super.

Przykładem stashing odniesień na temat samego obiektu, poprzez wdrożenie __new__ na klasy bazowej:

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 

I robi coś podobnego, ale za pomocą meta-klasę i stashing z __class referencje na obiektach klasy sami:

Running to wszystko razem, jako jeden plik źródłowy:

% 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> 
Powiązane problemy