2010-05-03 5 views
7

Muszę znaleźć elegancki sposób na zrobienie 2 rodzajów miksów.Co to są eleganckie sposoby na MixIns w Pythonie?

pierwsze:

class A(object): 
    def method1(self): 
     do_something() 

Teraz, MixInClass powinny method1 to zrobić: do_other() ->A.method1() ->do_smth_else() - czyli w zasadzie "wrap" starszej funkcji. Jestem prawie pewien, że musi istnieć dobre rozwiązanie tego.

drugie:

class B(object): 
    def method1(self): 
     do_something() 
     do_more() 

W tym przypadku, chcę MixInClass2 aby móc wprowadzić się między do_something() i do_more(), tj .: do_something() ->MixIn.method1 ->do_more(). Rozumiem, że to prawdopodobnie wymagałoby modyfikacji class B - to jest w porządku, szukając najprostszych sposobów, aby to osiągnąć.

To są dość trywialne problemy i faktycznie je rozwiązałem, ale moje rozwiązanie jest skażone.

Najpierw użyj numeru self._old_method1 = self.method1(); self.method1() = self._new_method1(); i napisz _new_method1(), który dzwoni pod numer _old_method1().

Problem: wiele składników MixIns zmieni nazwę na _old_method1 i jest nieelegancka.

Drugi miks W jednym rozwiązano, tworząc metodę atrapową call_mixin(self): pass i wstrzykując ją między wywołaniami i definiując self.call_mixin(). Ponownie nieelegancki i przerwie wiele MixIns ..

Wszelkie pomysły?


Dzięki Boldewyn, Znalazłem eleganckie rozwiązanie pierwsza (ja zapomniałem można utworzyć dekoratorów on-the-fly, bez modyfikowania oryginalnego kodu):

class MixIn_for_1(object): 
    def __init__(self): 
     self.method1 = self.wrap1(self.method1) 
     super(MixIn_for_1, self).__init__() 

    def wrap1(self, old): 
     def method1(): 
      print "do_other()" 
      old() 
      print "do_smth_else()" 
     return method1 

wciąż szukają na pomysły na drugie (ten pomysł nie pasuje, ponieważ muszę wstrzykiwać wewnątrz starej metody, a nie na zewnątrz, tak jak w tym przypadku).


Roztwór do sekundę poniżej, zastępując "pass_func" z lambda:0.

+0

Na drugie pytanie, czy mixin.method1 potrzebuje parametrów, a jeśli tak, to czy te parametry zależą od zmiennych/wartości zwracanych w do_something()? I wreszcie, czy do_more() potrzebuje wyniku twojego mixina? – KillianDS

+0

Nie, do_more() nie wymaga wyniku. Argumenty? Prawdopodobnie potrzebuję tylko "ja", które i tak powinno zostać przekazane. –

Odpowiedz

3

Oto kolejny sposób na wdrożenie MixInClass1, MixinClass2:

dekoratorów przydatna, gdy trzeba owinąć wiele funkcji. Od MixinClass1 musi zawijać tylko jedną funkcję, myślę, że jest jaśniejsze małpa poprawkę:

Korzystanie podwójne podkreślenia dla __old_method1 i __method1 odgrywa ważną rolę w MixInClass1.Z powodu koniugowania nazw w języku Python użycie podwójnych podkreśleń lokalizuje te atrybuty na MixinClass1 i umożliwia używanie tych samych nazw atrybutów dla innych klas łączenia bez powodowania niepożądanych kolizji nazw.

class MixInClass1(object): 
    def __init__(self): 
     self.__old_method1,self.method1=self.method1,self.__method1 
     super(MixInClass1, self).__init__()   
    def __method1(self): 
     print "pre1()" 
     self.__old_method1() 
     print "post1()" 

class MixInClass2(object): 
    def __init__(self): 
     super(MixInClass2, self).__init__()   
    def method1_hook(self): 
     print('MixIn method1') 

class Foo(MixInClass2,MixInClass1): 
    def method1(self): 
     print "do_something()" 
     getattr(self,'method1_hook',lambda *args,**kw: None)() 
     print "do_more()" 

foo=Foo() 
foo.method1() 
+0

Pierwsze rozwiązanie jest dokładnie takie samo, jak to opisałem. Drugi jest sprytny, zakładając, że 'Foo' to' B', a pass_func to 'lambda: 0'. –

+0

@Slava: Jeśli użyjesz 'pass_func', to nie będziesz musiał zmieniać' Foo', jeśli później zdecydujesz się przekazać argumenty do 'method1_hook'. Jeśli użyjesz 'lambda: 0', będziesz musiał zmienić ją na' lambda x, y, z: 0'. – unutbu

+0

1. Co to jest 'pass_func'? 2. Nie możemy po prostu zrobić 'lambda * args, ** kwargs: 0'? –

5

Myślę, że można to zrobić w dość sprytny sposób, używając decorators. (PEP 318 też)

+0

Dzięki, że rozwiązuje mój pierwszy problem. Tak naprawdę myślałem o dekoratorach, ale zapomniałem, że mogę konstruować dekoratory jako deco (metoda) zamiast @deco. Ten ostatni wymagał modyfikacji oryginalnego kodu. –