2014-10-05 2 views
3

Мне сложно обсуждать вопрос, поэтому я опишу, что я пытаюсь сделать:Как остановить выполнение функции по строкам?

Я пишу тестовое решение для определенного продукта, скажем, микроволновую печь. Целью является измерение всех видов ресурсов, в то время как микроволновая печь используется. Для этого я написал контекстный менеджер под названием measure().

Используя микроволновую API я написал функцию, которая имитирует основной случай использования в микроволновой печи, и украсил его с функцией, которая делает все измерения:

def test_function(fn): 
    def wrapper(*args, **kwargs): 
     with measure(): 
      fn(*args, **kwargs) 

    return wrapper 

@test_function 
def prepare_food(): 
    microwave = Microwave() 
    microwave.open_door() 
    microwave.insert_food() 
    microwave.close_door() 
    microwave.turn_on(seconds = 60) 

prepare_food() 

Конечно микроволновый только один пример и у меня много таких «микроволн».

Через несколько дней тестирования и измерение моей команды решил, что они хотят, чтобы измерить каждое действие отдельно, то есть мой тест функцию теперь будут выглядеть следующим образом:

def test_prepare_food(): 
    microwave = Microwave() 
    actions = { 
      microwave.open_door : [], 
      microwave.insert_food : [], 
      microwave.close_door() : [], 
      microwave.turn_on : [60] 
      } 
    for (action, args) in actions.items(): 
      with measure(): 
       action(*args) 

Проблема с новым test_prepare_food является то, что в настоящее время для каждая функция prepare_food, мне нужно добавить еще одну функцию test_* (и у меня много).

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

Edit: Целью является решение, которое не будет зависеть от реализации prepare_food(), так что изменение prepare_food() не потребует дополнительных изменений. Кроме того, решение не должно влиять на методы, которые не используются prepare_food.

Другими словами, я хотел бы иметь возможность «вступить в» prepare_food и иметь возможность выполнять код до и после каждой строки. Это похоже на то, что сделано во время отладки, но я не мог найти ничего подобного.

Любые идеи?

Спасибо!

+0

Просто, чтобы получить это из пути рано: Вы не просто хотите измерить * время выполнения * для каждой строки, правильно? (Потому что это будет работа для профилировщика). Вам также необходимо отслеживать некоторые другие ресурсы, требующие измерения пользовательского кода? –

+0

@LukasGraf - это правильно, я измеряю другие ресурсы, такие как тепло, которое производит микроволновая печь, и т. Д. – NStiner

ответ

2

Вы можете украсить каждый экземпляр класса микроволн методом measure. Это может быть выполнено с помощью декоратора класса в классе микроволн. В этом примере я просто модифицирую класс Microwave с помощью метода. Методами, которые выполняют работу, являются wrap и wrap_method.

import inspect 

class measure(object): 
    def __enter__(self): 
     print "Enter measure." 
    def __exit__(self, *args): 
     print "Exit measure." 

class Microwave(object): 
    def f(self, x): 
     print "f: %s" % x 
    def g(self, x): 
     print "g: %s" % x 

def wrap_method(method): 
    def wrapper(*args, **kwargs): 
     with measure(): 
      method(*args, **kwargs) 
    return wrapper 

def wrap(cls): 
    for name, method in inspect.getmembers(cls, predicate=inspect.ismethod): 
     setattr(cls, name, wrap_method(method)) 

wrap(Microwave) 
m = Microwave() 
m.f(1) 
m.g(2) 

Выход:

Enter measure. 
f: 1 
Exit measure. 
Enter measure. 
g: 2 
Exit measure. 
+0

+1 для использования 'inspect.getmembers', но это не совсем то, что я ищу - 1) это зависит от реализация 'prepare_food'; 2) он изменит все методы «Микроволны», включая те, которые не используются мной напрямую. Я отредактировал вопрос, чтобы упомянуть об этих требованиях. – NStiner

Смежные вопросы