2017-05-13 63 views
11

Czy istnieje prosty sposób na zrobienie czegoś na początku i na końcu każdej funkcji w klasie? Zajrzałem do __getattribute__, ale nie sądzę, że mogę go użyć w tej sytuacji?Czy coś na początku i na końcu metody

Oto uproszczona wersja tego, co próbuję zrobić:

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

Zastanawiasz się, dlaczego chcesz to zrobić? – abccd

+1

@abccd Czy masz na myśli, dlaczego chciałby użyć 'self.busy' w ten sposób? –

+0

Tak, to właśnie miałem na myśli – abccd

Odpowiedz

11

Można używać dekoratorów (jeśli ich nie znam można odwołać się do PEP-318):

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): 
     ... 

Możesz chcieć użyć functools.wraps, jeśli chcesz, aby udekorowana metoda "wyglądała jak" oryginalna metoda. @decorator jest po prostu cukier syntaktyczny, można również zastosować dekorator extenso:

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" 

W przypadku naprawdę chcesz, aby zastosować go do wszystkich metod można dodatkowo użyć klasy dekoratora:

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

Strzelaj !, Pokieruj mną do tego ;-) –

+0

To działa idealnie! Dziękuję Ci! Ponadto, czy musi on być nazywany 'dekoratorem', czy może być nazywany cokolwiek? – diligar

+0

@diligar Możesz nazwać to, co chcesz. Upewnij się tylko, że podane przez Ciebie imię ma znaczenie. –

1

Jako alternatywą dla zaakceptowanej odpowiedzi, jeśli chcesz, aby ta dekoracja była dostępna tylko na przykładowe metody, możesz użyć __getattribute__.

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.`. 
     ... 
  • Wymaga to object i nie będzie działać na zajęcia w starym stylu w Pythonie 2.
  • callable został usunięty w Pythonie 3.0, ale wrócił w 3.2. Alternatywnie można użyć isinstance(obj, collections.Callable).

Jeśli chcesz zawinąć metody klasy i metody statyczne inaczej, można dziedziczyć ze zwyczajem 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 

Powyższy metaclass=Meta Python 3 jest składnia. W Pythonie 2 należy go zdefiniować jako:

class Thing(object): 
    __metaclass__ = Meta