2009-05-05 13 views
14

Quindi, penso che il codice probabilmente spiega quello che sto cercando di fare meglio di quanto possa a parole, quindi ecco qui:classe astratta + mixin + ereditarietà multipla in python

import abc 

class foo(object): 
    __metaclass__ = abc.ABCMeta 
    @abc.abstractmethod 
    def bar(self): 
     pass 


class bar_for_foo_mixin(object): 
    def bar(self): 
     print "This should satisfy the abstract method requirement" 


class myfoo(foo, bar_for_foo_mixin): 
    def __init__(self): 
     print "myfoo __init__ called" 
     self.bar() 

obj = myfoo() 

Il risultato:

TypeError: Can't instantiate abstract class myfoo with abstract methods bar 

Sto cercando di ottenere la classe di missaggio per soddisfare i requisiti della classe astratta/di interfaccia. Cosa mi manca?

risposta

15

L'eredità non dovrebbe essere l'inverso? Nel MRO foo arriva prima di bar_for_foo_mixin, e quindi giustamente si lamenta. Con class myfoo(bar_for_foo_mixin, foo) dovrebbe funzionare.

E non sono sicuro che il design della classe sia il modo giusto per farlo. Poiché si utilizza un mixin per implementare bar, potrebbe essere meglio non derivare da foo e registrarlo semplicemente con la classe 'foo' (ad esempio foo.register(myfoo)). Ma questo è solo il mio istinto.

Per completezza, ecco lo documentation for ABCs.

+0

Buona chiamata, cambiando l'ordine di successione fa il trucco . P.S. il codice è stato semplificato per illustrare un punto. Il mio scenario del mondo reale è molto più complesso con un sacco di potenziali mixin e molti myfoos. – mluebke

0

penso (testato in casi simili), che invertendo le baseclasses funziona:

class myfoo(bar_for_foo_mixin, foo): 
    def __init__(self): 
     print "myfoo __init__ called" 
     self.bar() 

così nel MRO() sarebbe trovare una versione concreta di bar() prima che trovi quello astratto. Non so se questo è effettivamente ciò che accade in background però.

Cheers, Lars

PS: il codice che ha lavorato in python 2.7 (Python 3 ha un modo diverso per impostare metaclassi) era:

class A(object): 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractmethod 
    def do(self): 
     pass 

class B(object): 
    def do(self): 
     print "do" 

class C(B, A): 
    pass 

c = C() 
+0

Grazie per questa risposta. In py27, il codice che ha funzionato effettivamente impone la presenza di 'do()' nella classe 'C'. Tuttavia, in py3 (ho provato solo in py35), questo non è più il caso. Nella classe 'B' puoi sostituire il corpo con' pass', e l'istanza 'c' sarà felicemente creata. Sai perché potrebbe essere? –

+0

@cjrh: intendi il corpo di B o il corpo di B.do? Se fosse il primo, sarebbe davvero strano perché sarebbe un po 'sfidare lo scopo di abc. – Lars

+0

Basta provare il tuo codice sotto "PS: il codice che ha funzionato era", ma cambia 'B' in' classe B (oggetto): pass'. Vedrai che l'istanza 'c' è stata creata con successo. Ho provato su Python 3.6, stesso risultato. –