2017-05-13 22 views
11

Gibt es eine einfache Möglichkeit, am Anfang und am Ende jeder Funktion in einer Klasse etwas zu tun? Ich habe in __getattribute__ geschaut, aber ich denke nicht, dass ich es in dieser Situation verwenden kann?Etwas am Anfang und am Ende der Methoden tun

Hier ist eine vereinfachte Version von dem, was ich versuche zu tun:

class Thing(): 
    def __init__(self): 
     self.busy = False 

    def func_1(self): 
     if self.busy: 
      return None 
     self.busy = True 
      ... 
     self.busy = False 

    def func_2(self): 
     if self.busy: 
      return None 
     self.busy = True 
      ... 
     self.busy = False 
    ... 
+1

Ich frage mich nur, warum möchten Sie das tun? – abccd

+1

@abccd Meinst du, warum er 'self.busy' so benutzen möchte? –

+0

Ja, das ist, was ich meinte – abccd

Antwort

11

Sie verwenden Dekorateure können (wenn Sie nicht wissen, dass Sie zu PEP-318 beziehen):

def decorator(method): 
    def decorated_method(self, *args, **kwargs): 
     # before the method call 
     if self.busy: 
      return None 
     self.busy = True 

     # the actual method call 
     result = method(self, *args, **kwargs) 

     # after the method call 
     self.busy = False 

     return result 

    return decorated_method 

class Thing(): 
    def __init__(self): 
     self.busy = False 

    @decorator 
    def func_1(self): 
     ... 

    @decorator 
    def func_2(self): 
     ... 

Vielleicht möchten Sie functools.wraps verwenden, wenn Sie möchten, dass die dekorierte Methode der ursprünglichen Methode "gleicht". Die @decorator ist nur syntaktischer Zucker, können Sie auch das Dekorateur gelten ausdrücklich:

class Thing(): 
    def __init__(self): 
     self.busy = False 

    def func_1(self): 
     ... 

    func_1 = decorator(func_1) # replace "func_1" with the decorated "func_1" 

Im Fall, dass Sie es wirklich Sie können zusätzlich eine Klasse verwenden, Dekorateur für alle Methoden anzuwenden:

def decorate_all_methods(cls): 
    for name, method in cls.__dict__.items(): 
     if name.startswith('_'): # don't decorate private functions 
      continue 
     setattr(cls, name, decorator(method)) 
    return cls 

@decorate_all_methods 
class Thing(): 
    def __init__(self): 
     self.busy = False 

    def func_1(self): 
     ... 

    def func_2(self): 
     ... 
+2

Shoot !, Beat mich dazu ;-) –

+0

Dies funktioniert perfekt! Vielen Dank! Muss es auch "Dekorateur" genannt werden, oder kann es irgendwas heißen? – diligar

+0

@diligar Sie können es nennen, was Sie wollen. Stellen Sie nur sicher, dass der Name, den Sie ihm geben, sinnvoll ist. –

1

Als Alternativ zur akzeptierten Antwort, wenn Sie möchten, dass diese Dekoration nur für Beispielmethoden gilt, können Sie __getattribute__ verwenden.

class Thing(object): 
    def __init__(self): 
     self.busy = False 

    def __getattribute__(self, name): 
     attr = object.__getattribute__(self, name) 
     if callable(attr) and not name.startswith('_') and attr.__self__ == self: 
      attr = decorator(attr) 

     return attr 

    def func_1(self): 
     # instance method will be wrapped by `decorator` 
     ... 

    @classmethod 
    def class_func(cls): 
     # class method will not be wrapped by `decorator` 
     # when called using `self.`, `cls.` or `Thing.`. 
     ... 

    @staticmethod 
    def static_func(): 
     # static method will not be wrapped by `decorator` 
     # when called using `Thing.`. 
     ... 
  • Dies erfordert object und wird für im alten Stil Klassen in Python nicht funktioniert 2.
  • callable wurde in Python 3.0 entfernt, kehrte aber in 3.2. Alternativ kann isinstance(obj, collections.Callable) verwendet werden.

Wenn Sie möchten, dass anders Klassenmethoden und statische Methoden wickeln, könnte man von einem benutzerdefinierten erben typemetaclass:

class Meta(type): 
    def __getattribute__(*args): 
     print("staticmethod or classmethod invoked") 
     return type.__getattribute__(*args) 


class Thing(object, metaclass=Meta): 
    ... 
    def __getattribute__(self, name): 
     attr = object.__getattribute__(self, name) 
     if callable(attr) and not name.startswith('_'): 
      if attr.__self__ == self: 
       attr = decorator(attr) 
      else: 
       attr = Meta.__getattribute__(Thing, name) 

     return attr 

Die obigen metaclass=Meta Python 3 Syntax ist. In Python 2 muss es wie folgt definiert werden:

class Thing(object): 
    __metaclass__ = Meta 
Verwandte Themen