2014-09-13 4 views
2

Есть ли возможность уловить события «до/после вызова функции» для всех функций класса, не украшая каждую из этих функций? Может быть, какой-то классный декоратор? Другими словами, для такого кода, я хотел бы получить следующий вывод:Поймать события «до/после вызова функции» для всех функций класса

class Foo: 
    def func1(): 
     print('1') 

    def func2(): 
     print('2') 

c = Foo() 
c.func1() 
c.func2() 

# Output I would like to get: 
# func1 called 
# 1 
# func1 finished 
# func2 called 
# 2 
# func2 finished 

мне это нужно не для трассировки. В классе, работающем с асинхронными функциями, мне нужно знать, вызывалась ли какая-либо функция до того, как была закончена другая функция.

+1

положить в печать заявления? Например, –

+0

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

ответ

12

Да, вы можете написать декоратор класса; следующее позволит вам украсить каждую из функций в классе:

def decorate_all_functions(function_decorator): 
    def decorator(cls): 
     for name, obj in vars(cls).items(): 
      if callable(obj): 
       try: 
        obj = obj.__func__ # unwrap Python 2 unbound method 
       except AttributeError: 
        pass # not needed in Python 3 
       setattr(cls, name, function_decorator(obj)) 
     return cls 
    return decorator 

выше класс декоратора применяет данную функцию декоратора для всех вызываемых объектов по классу.

Предположим, вы декоратор, который печатает имя вызываемой функции до и после:

from functools import wraps 

def print_on_call(func): 
    @wraps(func) 
    def wrapper(*args, **kw): 
     print('{} called'.format(func.__name__)) 
     try: 
      res = func(*args, **kw) 
     finally: 
      print('{} finished'.format(func.__name__)) 
     return res 
    return wrapper 

то класс декоратора может быть применен с:

@decorate_all_functions(print_on_call) 
class Foo: 
    def func1(self): 
     print('1') 

    def func2(self): 
     print('2') 

Демо:

>>> @decorate_all_functions(print_on_call) 
... class Foo: 
...  def func1(self): 
...   print('1') 
...  def func2(self): 
...   print('2') 
... 
>>> c = Foo() 
>>> c.func1() 
func1 called 
1 
func1 finished 
>>> c.func2() 
func2 called 
2 
func2 finished 
1

Если вы не хотите украшать все классы, которые хотите иметь эту функциональность, u можете попробовать метапрограммирование, чтобы изменить методы во время создания, чтобы запустить предварительную/пост-операцию, детали находятся в ответе на этот вопрос. How to run a method before/after all class function calls with arguments passed?