Профилирование python с использованием line_profiler - умный способ удаления операторов @profile на лету?

Я хочу использовать отличный line_profiler, но только некоторое время. Чтобы сделать это, я добавляю

@profile

перед каждым вызовом функции, например

@profile
def myFunc(args):
    blah
    return

и выполните

kernprof.py -l -v mycode.py args

Но я не хочу, чтобы каждый раз декорировать @profile вручную, потому что большую часть времени я хочу выполнить код без них, и получаю исключение, если я пытаюсь их включить, например,

mycode.py args

Есть ли счастливая среда, где я могу динамически удалять декораторов на основе какого-либо условия switch/argument, без необходимости делать что-то вручную и/или изменять каждую функцию слишком много?

Ответ 1

Вместо того, чтобы удалять строки декоратора @profile, укажите свою собственную сквозную версию no-op.

В проект можно добавить следующий код:

import __builtin__

try:
    __builtin__.profile
except AttributeError:
    # No line profiler, provide a pass-through version
    def profile(func): return func
    __builtin__.profile = profile

Импортируйте это перед любым кодом с помощью декоратора @profile, и вы можете использовать код с активным профилиром линии или без него.

Поскольку фиктивный декоратор является сквозной функцией, производительность исполнения не затрагивается (только импортная производительность настолько сильно затронута).

Если вам не нравится возиться со встроенными модулями, вы можете сделать это отдельным модулем; скажем profile_support.py:

import __builtin__

try:
    profile = __builtin__.profile
except AttributeError:
    # No line profiler, provide a pass-through version
    def profile(func): return func

(без присвоения __builtin__.profile) и используйте from profile_support import profile в любом модуле, который использует декоратор @profile.

Ответ 2

Комментарий, который вырос, чтобы стать вариантом ответа @Martijin Pieters.

Я предпочитаю вообще не включать __builtin__. Без комментария было бы практически невозможно, чтобы кто-то еще догадался, что задействован line_profiler, без априори, зная это.

Рассматривая kernprof line 199, достаточно создать экземпляр LineProfiler.

try:
    from line_profiler import LineProfiler
    profile = LineProfiler()
except ImportError:
    def profile(func):
        return func

Импорт (явный) лучше, чем глобальная модификация builtins (неявная). Если профилирующие декораторы являются постоянными, то их происхождение должно быть ясным в самом коде.

При наличии line_profiler вышеприведенный подход обертывает украшенные функции профайлерами при каждом запуске, независимо от того, выполняется ли оно kernprof. Этот побочный эффект может быть нежелательным.

Ответ 3

Вам не нужно импортировать __builtins__/builtins или LineProfiler вообще, вы можете просто полагаться на NameError при поиске profile:

try:
    profile
except NameError:
    profile = lambda x: x

Однако это должно быть включено в каждый файл, который использует profile, но он (постоянно) не изменяет глобальное состояние (встроенные) Python.

Ответ 4

Я использую следующую модифицированную версию с Python 3.4

try:
    import builtins
    profile = builtins.__dict__['profile']
except KeyError:
    # No line profiler, provide a pass-through version
    def profile(func): return func