2016-02-08 24 views
7

voglio definire un mix-in di un namedtuple e una classe base con definisce e abstract method:Mix-in di classe astratta e namedtuple

import abc 
import collections 

class A(object): 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractmethod 
    def do(self): 
     print("U Can't Touch This") 

B = collections.namedtuple('B', 'x, y') 

class C(B, A): 
    pass 

c = C(x=3, y=4) 
print(c) 
c.do() 

Da quello che ho capito leggendo i documenti e other examples I have seen, c.do() dovrebbe genera un errore, in quanto la classe C non implementa do(). Tuttavia, quando lo eseguo ... funziona:

B(x=3, y=4) 
U Can't Touch This 

Devo trascurare qualcosa.

+1

Quale versione di python stai usando? I metaclassi sono invocati in modi diversi e incompatibili in python 2 versus python 3. Se stai usando python 3, la tua classe non è in realtà una classe astratta. – Dunes

+0

Ho provato questo con entrambi Python 2 e 3. Stesso comportamento. – plok

risposta

5

Quando si osserva l'ordine di risoluzione dei metodi di C, si vede che B arriva prima dello A nell'elenco. Ciò significa che quando si istanzia lo C il metodo __new__ di B verrà chiamato per primo.

Questa è l'implementazione di namedtuple.__new__

def __new__(_cls, {arg_list}): 
    'Create new instance of {typename}({arg_list})' 
    return _tuple.__new__(_cls, ({arg_list})) 

Si può vedere che non supporta l'ereditarietà cooperativa, perché rompe la catena e chiama semplicemente tuple s metodo __new__. In questo modo il metodo ABCMeta.__new__che verifica i metodi astratti non viene mai eseguito (sempre dove sia) e non può controllare i metodi astratti. Quindi l'istanziazione non fallisce.

Ho pensato che invertire l'MRO avrebbe risolto quel problema, ma stranamente no. Investigherò un po 'di più e aggiornerò questa risposta.

+1

Probabilmente l'inversione dell'MRO non ha importanza perché 'B' implementa' __new__' e 'A' no. –

+1

ma sia 'namedtuple' che' ABCMeta' hanno un metodo '__new__'. Non è la prima ricerca di MRO come prima? – Wombatz

+1

Kinda. Vedere questo esempio: '>>> classe X (B, A): ... pass ... >>> X .__ mro__ (, . '>, , , ) >>> classe Y (A, B): ... pass .. >>> Y .__ mro__ (, , , , ) '. In ogni caso, la tupla viene chiamata prima, perché alla fine entrambi derivano dall'oggetto. –