Каковы элегантные способы сделать MixIns в Python? - PullRequest
7 голосов
/ 03 мая 2010

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

Первый:

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 - это нормально, просто ищу самые простые способы для достижения этой цели.

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

Первый, используя self._old_method1 = self.method1(); self.method1() = self._new_method1(); и записав _new_method1(), который вызывает _old_method1().

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

Второй MixIn был решен путем создания фиктивного метода call_mixin(self): pass и внедрения его между вызовами и определения self.call_mixin(). Снова не элегантный и сломается на нескольких 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.

Ответы [ 2 ]

5 голосов
/ 03 мая 2010

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

3 голосов
/ 03 мая 2010

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

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

Использование двойных подчеркиваний для __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()
...