2012-03-09 12 views
6

Uważam, że wiele klas, które piszę w Pythonie, zawiera mały zestaw zmiennych, które naprawdę chciałbym zobaczyć, gdy zadzwonię pod numer str(), i że przepisanie __str__(self) dla każdego jest raczej uciążliwe. Zatem ja gotowane następujące wstawek,Python Mixin dla __str__and Metoda Resolution Order

class StrMixin(object): 
    ''' 
    Automatically generate __str__ and __repr__ 
    ''' 
    def __str__(self): 
    import types 
    name = self.__class__.__name__ + ': ' 
    attrs = [ '{}={}'.format(k,v) for (k,v) in self.__dict__.items() ] 
    return name + ', '.join(attrs) 

    def __repr__(self): 
    return str(self) 

Jeśli jednak napisać klasę,

class C(object, StrMixin): 
    pass 

otrzymuję następujący błąd na konkretyzacji,

TypeError: Error when calling the metaclass bases 
    Cannot create a consistent method resolution 
order (MRO) for bases object, StrMixin 

To prawda, w tym object tutaj jest zbędny, ale co tu się naprawdę dzieje?

+1

Jeśli chcesz po prostu obejść ten problem, należy rozważyć dokonanie dekorator zamiast mixin. –

Odpowiedz

1

Sam odpowiedziałeś na pytanie - drugi object jest zbędny. Klasa C ma dwie podstawy: obiekt i StrMixin. Jednak baza StrMixin jest również obiektem, więc nie ma pewności co do tego, który obiekt powinien najpierw rozwiązać. MRO oblicza go jako (C, STRMixin, obiekt, obiekt), który ma zduplikowane obiekty. W tym konkretnym przypadku wydaje się oczywiste, jakie powinno być rozwiązanie, ale należy dodać kilka klas i MRO może stać się mniej jasne. Na przykład.

class A(object): 
    pass 
class B(object, A): 
    pass 
class C(object, A): 
    pass 
class D(object, B, C): 
    pass 
class E(object, A, D): 
    pass 

Co to jest MRO dla E? Cokolwiek to jest, jest bardzo skomplikowane, ma duplikaty i prawdopodobnie kilka pętli.

MRO wyjaśniono całkiem dobrze here, a twój konkretny przypadek zajmuje około dwie trzecie w dół strony, pierwszy przykład pod "Zlecenia rozwiązywania złych metod".

+0

Czy możesz wyjaśnić "obiekt przychodzi StrMixin przychodzi po obiekt"? – duckworthd

+1

W rzeczywistości duplikaty nie stanowią problemu, ponieważ algorytm MRO Pythona usuwa te duplikaty. IIRC, algorytm MRO nie uległ zmianie w ogóle w Pythonie 3, a przykładowy kod OP daje taki sam błąd w 3.2. –

+0

To dość zagmatwany temat, ale mam nadzieję, że teraz jest to bardziej przejrzyste. – aquavitae

11

Podczas definiowania:

class StrMixin(object): 
    ... 

Kompilator wie, że StrMixin przychodzi przed object w MRO klasy za.

Kiedy zrobić:

class C(object, StrMixin): 
    pass 

Powiedziałeś kompilator, że object przychodzi przed StrMixin w MRO. Ale object musi również przyjść po StrMixin, więc musiałby pojawić się dwa razy w MRO, a to nie jest dozwolone.

Jeśli powiesz:

class C(StrMixin, object): 
    pass 

wtedy MRO jest po prostu C, StrMixin, object który spełnia kolejność nałożone obu klas. Nie ma dublowania, ponieważ chociaż odwołanie do object jest przywoływane dwukrotnie, nie ma konfliktu między definicjami.

+1

+1. Twoja odpowiedź jest znacznie wyraźniejsza niż moja, więc nawet nie zawracałem sobie głowy poprawianiem moich błędów. –

0

Wielokrotne dziedziczenie może być naginaniem umysłu. Aby zachować prostotę dziedziczenia podczas używania mixin, możesz zdefiniować funkcję poza klasą i przypisać ją do klasy, gdy ją zdefiniujesz.

class StrMixin:  # class here used only as a namespace 

    @staticmethod # not needed with Python 3 
    def __str__(self): 
     name = self.__class__.__name__ + ': ' 
     attrs = [ '{}={}'.format(k,v) for (k,v) in self.__dict__.items() ] 
     return name + ', '.join(attrs) 
    __repr__ = __str__ 

class C(object): 
    __str__, __repr__ = StrMixin.__str__, StrMixin.__repr__ 

Lub jeśli przechowywać swoje funkcje wstawek w module, a następnie można go używać w swojej klasie tak:

class C(object): 
    from StrMixin import __str__, __repr__