Печать имен переменных, переданных в функцию - PullRequest
5 голосов
/ 30 марта 2012

В некоторых случаях я хочу напечатать вывод в стиле отладки следующим образом:

# module test.py
def f()
  a = 5
  b = 8
  debug(a, b) # line 18

Я хочу, чтобы функция debug выводила следующее:

debug info at test.py: 18
function f
a = 5
b = 8

Ядумая, что это возможно, используя модуль inspect, чтобы найти кадр стека, затем найти соответствующую строку, найти исходный код в этой строке и получить имена аргументов оттуда.Имя функции можно получить, переместив один кадр стека вверх.(Значения аргументов легко получить: они передаются непосредственно в функцию debug.)

Я на правильном пути?Могу ли я сослаться на какой-нибудь рецепт?

Ответы [ 4 ]

3 голосов
/ 30 марта 2012

Вы могли бы сделать что-то вроде следующего:

import inspect

def debug(**kwargs):
  st = inspect.stack()[1]
  print '%s:%d %s()' % (st[1], st[2], st[3])
  for k, v in kwargs.items():
    print '%s = %s' % (k, v)

def f():
  a = 5
  b = 8
  debug(a=a, b=b) # line 12

f()

Это напечатает:

test.py:12 f()
a = 5
b = 8
1 голос
/ 30 марта 2012

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

import inspect

def f():
  a = 5
  b = 8
  debug(a, b)


def debug(a, b):
    print inspect.getargspec(debug)
f()
1 голос
/ 30 марта 2012

Как правило, вы делаете это правильно, хотя было бы проще использовать АОП для такого рода задач. По сути, вместо того, чтобы каждый раз вызывать «debug» для каждой переменной, вы можете просто украсить код аспектами, которые делают определенные вещи для определенных событий, например, при вводе функции для печати переданных переменных и ее имени.

Пожалуйста, обратитесь к этому сайту и старому, так что post для получения дополнительной информации.

0 голосов
/ 30 марта 2012

Это действительно сложно. Позвольте мне попытаться дать более полный ответ, повторно используя этот код , и подсказку о getargspec в ответе Сентила, которая меня как-то сработала. Кстати, getargspec устарела в Python 3.0, и вместо нее следует использовать getfullarcspec. .

Это работает для меня на Python 3.1.2 как с явным вызовом функции отладки, так и с использованием декоратора:

# from: https://stackoverflow.com/a/4493322/923794
def getfunc(func=None, uplevel=0):
    """Return tuple of information about a function

    Go's up in the call stack to uplevel+1 and returns information
    about the function found.

    The tuple contains
      name of function, function object, it's frame object,
      filename and line number"""
    from inspect import currentframe, getouterframes, getframeinfo
    #for (level, frame) in enumerate(getouterframes(currentframe())):
    #    print(str(level) + ' frame: ' + str(frame))
    caller = getouterframes(currentframe())[1+uplevel]
    # caller is tuple of:
    #  frame object, filename, line number, function
    #  name, a list of lines of context, and index within the context
    func_name = caller[3]
    frame = caller[0]
    from pprint import pprint
    if func:
        func_name = func.__name__
    else:
        func = frame.f_locals.get(func_name, frame.f_globals.get(func_name))
    return (func_name, func, frame, caller[1], caller[2])


def debug_prt_func_args(f=None):
    """Print function name and argument with their values"""
    from inspect import getargvalues, getfullargspec
    (func_name, func, frame, file, line) = getfunc(func=f, uplevel=1)
    argspec = getfullargspec(func)
    #print(argspec)
    argvals = getargvalues(frame)
    print("debug info at " + file + ': ' + str(line))
    print(func_name + ':' + str(argvals))   ## reformat to pretty print arg values here
    return func_name



def df_dbg_prt_func_args(f):
    """Decorator: dpg_prt_func_args - Prints function name and arguments

    """
    def wrapped(*args, **kwargs):
        debug_prt_func_args(f) 
        return f(*args, **kwargs) 
    return wrapped

Использование:

@df_dbg_prt_func_args
def leaf_decor(*args, **kwargs):
    """Leaf level, simple function"""
    print("in leaf")


def leaf_explicit(*args, **kwargs):
    """Leaf level, simple function"""
    debug_prt_func_args()
    print("in leaf")


def complex():
    """A complex function"""
    print("start complex")
    leaf_decor(3,4)
    print("middle complex")
    leaf_explicit(12,45)
    print("end complex")


complex()

и отпечатки:

start complex
debug info at debug.py: 54
leaf_decor:ArgInfo(args=[], varargs='args', keywords='kwargs', locals={'args': (3, 4), 'f': <function leaf_decor at 0x2aaaac048d98>, 'kwargs': {}})
in leaf
middle complex
debug info at debug.py: 67
leaf_explicit:ArgInfo(args=[], varargs='args', keywords='kwargs', locals={'args': (12, 45), 'kwargs': {}})
in leaf
end complex

Декоратор немного обманывает: поскольку в wrapped мы получаем те же аргументы, что и сама функция, не имеет значения, что мы находим и сообщаем ArgSpec wrapped в getfunc и debug_prt_func_args. Этот код можно немного украсить, но теперь он работает хорошо для простых тестовых случаев отладки, которые я использовал.

Еще один трюк, который вы можете сделать: если вы раскомментируете for -loop в getfunc, вы можете увидеть, что inspect может дать вам «контекст», который действительно является строкой исходного кода, где вызывается функция. Этот код, очевидно, не показывает содержимое какой-либо переменной, переданной вашей функции, но иногда он уже помогает узнать, какое имя переменной используется на один уровень выше вызываемой функции.

Как видите, с декоратором вам не нужно менять код внутри функции.

Вероятно, вы захотите напечатать аргументы. Я оставил в функции необработанный текст (а также закомментированный оператор печати), чтобы с ним было легче поиграться.

...