2010-05-03 4 views
7

Мне нужно найти элегантный способ сделать 2 вида MixIns.Каковы элегантные способы сделать MixIns в Python?

Первое:

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

Теперь, MixInClass должен сделать method1 это сделать: do_other() ->A.method1() ->do_smth_else() - то есть в основном "обернуть" старую функцию. Я уверен, что должно быть хорошее решение.

Второе:

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

В этом случае, я хочу MixInClass2, чтобы иметь возможность вводить себя между do_something() и do_more(), т.е .: do_something() ->MixIn.method1 ->do_more(). Я понимаю, что, вероятно, это потребует модификации class B - все в порядке, просто ищет простейшие способы достижения этого.

Это довольно тривиальные проблемы, и я их фактически решил, но мое решение испорчено.

Fisrt one используя self._old_method1 = self.method1(); self.method1() = self._new_method1(); и написание _new_method1(), что звонки на _old_method1().

Проблема: несколько MixIns переименуются в _old_method1, и это неэлегантно.

Second MixIn был решен путем создания пустого метода call_mixin(self): pass и ввода его между вызовами и определения self.call_mixin(). Опять inelegant и будет ломаться на нескольких MixIns ..

Любые идеи?


Благодаря Boldewyn, я нашел элегантное решение первой (я забыл, что вы можете создавать декораторы на лету, без изменения исходного кода):

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 

Все еще ищете для идей для второго (эта идея не подходит, так как мне нужно вводить внутри старого метода, а не снаружи, как в этом случае).


Раствор для второго ниже, заменив "pass_func" с lambda:0.

+0

Для вашего второго вопроса, нужны ли mixin.method1 параметры, и если да, то эти параметры зависят от переменных/возвращаемых значений в do_something()? И, наконец, do_more() нужен результат вашего mixin? – KillianDS

+0

Нет, do_more() не нуждается в результатах. Аргументы? Мне, вероятно, нужно только «я», которое должно быть передано в любом случае. –

ответ

3

Вот еще один способ реализации MixInClass1, MixinClass2:

Декораторы полезны, когда вам нужно обернуть много функций. Поскольку MixinClass1 нужно обернуть только одна функцию, я думаю, что это понятнее обезьяньего пластырь:

Использование двойного подчеркивания для __old_method1 и __method1 играет полезную роль в MixInClass1.Из-за соглашения об именах Python использование двойных подчеркиваний локализует эти атрибуты до MixinClass1 и позволяет использовать те же самые имена атрибутов для других классов смешения, не вызывая нежелательных коллизий имен.

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

Первое решение - это то же самое, что я описал в вопросе. Второй умный, предполагая, что 'Foo' является' B', а pass_func - 'lambda: 0'. –

+0

@Slava: Если вы используете 'pass_func', вам не придется менять' Foo', если позже вы решите передать аргументы 'method1_hook'. Если вы используете 'lambda: 0', вам придется изменить его на нечто вроде' lambda x, y, z: 0'. – unutbu

+0

1. Что такое 'pass_func'? 2. Разве мы не можем просто делать лямбда-арги, ** kwargs: 0'? –

5

Я думаю, что это можно обрабатывать довольно путинским способом, используя decorators. (PEP 318 тоже)

+0

Спасибо, что решает мою первую проблему. Я действительно думал о декораторах, но я забыл, что я мог бы построить декораторы как deco (method), а не @deco. Последнее требовало модификации исходного кода. –

Смежные вопросы