2011-07-21 18 views
8

Próbuję utworzyć obiekt za pomocą metody run, która zostanie opakowana za pomocą metody _wrap_run. Chciałbym móc wywołać metodę i jej wrapper, po prostu wpisując metodę instance.run() i chciałbym móc podklasować obiekt, aby można było przesłonić metodę run() i nadal wykonywać zawijanie .Metoda klasy języka Python Wrap

Mówiąc prościej, chcę, aby ludzie mogli uzyskać podklasę A i nadpisać run(), ale nadal mają wywołania metody run(), wykonaj funkcję wrapper.

Mam pewne problemy z mechaniką tego. Czy ktoś ma jakieś sugestie dotyczące tego podejścia? Dziękuje za przeczytanie.

class A: 

    def run(self): 
     print "Run A" 
     return True 

    def _wrap_run(self): 
     print "PRE" 
     return_value = self.run() 
     print "POST" 
     return return_value 

    run = property(_wrap_run) 


a = A() 
a.run() 
""" 
Should Print: 
PRE 
Run A 
POST 
""" 


class B(A): 

    def run(self): 
     print "Run B" 
     return True 

b = B() 
b.run() 
""" 
Should Print: 
PRE 
Run B 
POST 
""" 
+1

Jaki jest sens "_wrap_run"? Czy to ma być jakiś dekorator? Co jest złego w korzystaniu z dekoratora? –

+1

Nie chce ozdabiać wszystkich klas pochodnych. – agf

Odpowiedz

12

Użyj metaclass.

class MetaClass(type): 
    @staticmethod 
    def wrap(run): 
     """Return a wrapped instance method""" 
     def outer(self): 
      print "PRE", 
      return_value = run(self) 
      print "POST" 
      return return_value 
     return outer 
    def __new__(cls, name, bases, attrs): 
     """If the class has a 'run' method, wrap it""" 
     if 'run' in attrs: 
      attrs['run'] = cls.wrap(attrs['run']) 
     return super(MetaClass, cls).__new__(cls, name, bases, attrs) 

class MyClass(object): 
    """Use MetaClass to make this class""" 
    __metaclass__ = MetaClass 
    def run(self): print 'RUN', 

myinstance = MyClass() 

# Prints PRE RUN POST 
myinstance.run() 

Teraz, jeśli inni ludzie podklasy MyClass, będą nadal się ich run() metody opakowane.

+0

To naprawdę fajne podejście i wygląda na to, że działa dobrze. –

3

Najłatwiej: make run opakowanie, i prywatny sposób na sterowanie ręczne być jeden.

class A(object): 
    def run(self): 
     print "PRE" 
     return_value = self._inner_run() 
     print "POST" 
     return return_value 

    def _inner_run(self): 
     print "Run A" 
     return True 

class B(A): 
    def _inner_run(self): 
     print "Run B" 
     return True 
+0

Dzięki za komentarz. Chciałbym, aby osoba implementująca run() w podklasie nie musiała się martwić o wykonywanie dodatkowych wywołań i poleganie na nich w celu poprawnego dodania wywołania funkcji. Dodatkowo podkreślona funkcja owijarki wykonuje pewne prace konserwacyjne, które chciałbym zachować "ukryte" przed klasą dziecka. Chociaż nie wiem, czy to, o co pytam, zadziała ... –

+0

@JoeJ: Udokumentuj poprawnie interfejs i nie ma znaczenia, czy podklasa musi przesłonić 'run' lub coś innego. –

+0

@Joe: bardzo często można zezwolić użytkownikowi na przesłonięcie '_run', zobacz [FormEncode' _to_python'] (http://formencode.org/Validator.html?highlight=_to_python) na przykład zamiast "run" – neurino

1

Co tam masz to w zasadzie decorator, więc dlaczego nie iść dalej i realizować _wrap_run jako dekorator i stosuje się go, gdy instacji funkcję?

+0

Byłem myśląc o zrobieniu z niego dekoratora, ale czy istnieje sposób na dekorowanie w klasie rodzica, więc osoba realizująca jakieś zajęcia dla dzieci nie musi dodawać dekoratora za każdym razem? –

+0

Nie, to wbrew temu, co python oznacza –

+0

Myślę, że to __exactly__ to, co oznacza Python. Czym różni się od pisania na klawiaturze lub menedżerów kontekstu? Mogę napisać "run()" w prosty sposób, a metaklass zajmie się szczegółami. – agf

1

Co inni ludzie zrobić

class A: 
    def do_run(self): 
     """Must be overridden.""" 
     raise NotImplementedError 
    def run(self, *args, **kw): 
     """Must not be overridden. 
     You were warned. 
     """ 
     print "PRE" 
     return_value = self.do_run(*args, **kw) 
     print "POST" 
     return return_value 

class B(A): 
    def do_run(self): 
     print "Run B" 
     return True 

To zwykle wystarcza.

Jeśli chcesz się martwić, że ktoś "to zepsuje", zatrzymaj się teraz. Nie trać czasu na martwienie się.

To jest Python. Wszyscy jesteśmy dorośli tutaj. Wszyscy złośliwi socjopaci złamią cały twój kod, kopiując go, zmieniając go, a następnie go łamiąc. Bez względu na to, co zrobisz, po prostu skopiują twój kod i zmodyfikują go, by złamać sprytne bity.

Wszyscy inni przeczytają Twój komentarz i będą trzymać się reguł. Jeśli chcą używać twojego modułu/pakietu/framework, będą współpracować.

+0

'run()' musi wywoływać 'do_run()', które musi akceptować argumenty. – agf

+0

Dzięki S.Lott. Wydaje się to podejściem z naciskiem. Tak, przypuszczam, że wystarczyłoby kilka dobrych komentarzy. –

+0

@Joe J: Oni ** są ** wystarczające. Nic więcej nie możesz zrobić, naprawdę. Coś bardziej złożonego to tylko ból głowy związany z utrzymaniem. I bez względu na to, ile napiszesz dodatkowego kodu, ktoś zawsze będzie z tym bałaganił. Napisz więc najmniejszy kod, którym możesz uciec. –