Возвращаемое значение при использовании cProfile

Я пытаюсь профилировать метод экземпляра, поэтому я сделал что-то вроде:

import cProfile

class Test():

    def __init__(self):
        pass

    def method(self):
        cProfile.runctx("self.method_actual()", globals(), locals())

    def method_actual(self):
        print "Run"

if __name__ == "__main__":
    Test().method()

Но теперь проблемы возникают, когда я хочу, чтобы "метод" возвращал значение, которое вычисляется методом "method_actual". Я действительно не хочу дважды называть метод method_actual.

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

Ответ 1

Я обнаружил, что вы можете это сделать:

prof = cProfile.Profile()
retval = prof.runcall(self.method_actual, *args, **kwargs)
prof.dump_stats(datafn)

Недостатком является то, что он недокументирован.

Ответ 2

Опция для любого произвольного кода:

import cProfile, pstats, sys
pr = cProfile.Profile()
pr.enable()

my_return_val = my_func(my_arg)

pr.disable()
ps = pstats.Stats(pr, stream=sys.stdout)
ps.print_stats()

Взято из https://docs.python.org/2/library/profile.html#profile.Profile

Ответ 3

Я боролся с той же проблемой и использовал функцию-оболочку для получения значений прямого возврата. Вместо

cP.runctx("a=foo()", globals(), locales())

Я создаю функцию-обертку

def wrapper(b):
  b.append(foo())

и профилировать вызов функции-обертки

b = []
cP.runctx("wrapper(b)", globals(), locals())
a = b[0]

извлечение результата вычисления foo из параметра out (b) впоследствии.

Ответ 4

Я думаю, @detly .runcall() - это в основном лучший ответ, но для полноты я просто хотел, чтобы ответ @ThomasH был независимым от функции:

def wrapper(b, f, *myargs, **mykwargs):
    try:
        b.append(f(*myargs, **mykwargs))
    except TypeError:
        print 'bad args passed to func.'

# Example run
def func(a, n):
    return n*a + 1

b = []
cProfile.runctx("wrapper(b, func, 3, n=1)", globals(), locals())
a = b[0]
print 'a, ', a

Ответ 5

Я создал декоратор:

import cProfile
import functools
import pstats

def profile(func):

    @functools.wraps(func)
    def inner(*args, **kwargs):
        profiler = cProfile.Profile()
        profiler.enable()
        try:
            retval = func(*args, **kwargs)
        finally:
            profiler.disable()
            with open('profile.out', 'w') as profile_file:
                stats = pstats.Stats(profiler, stream=profile_file)
                stats.print_stats()
        return retval

    return inner

Украсьте свою функцию или метод:

@profile
def somefunc(...):
   ...

Теперь эта функция будет профилирована.

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

import cProfile
import functools
import pstats

def profile(func):

    @functools.wraps(func)
    def inner(*args, **kwargs):
        profiler = cProfile.Profile()
        profiler.enable()
        try:
            retval = func(*args, **kwargs)
        finally:
            profiler.disable()
            profiler.dump_stats('profile.out')
        return retval

    return inner

Это небольшое улучшение по нескольким другим ответам на этой странице.