2012-01-13 2 views
7

В C/C++ я часто находил полезным при отладке для определения макроса, скажем ECHO(x), который печатает имя переменной и ее значение (то есть ECHO(variable) может печать variable 7). Вы можете получить имя переменной в макросе, используя оператор 'stringification' #, как описано here. Есть ли способ сделать это в Python?Найти имя переменной Python, которая была передана функции

Другими словами, я бы как функция

def echo(x): 
    #magic goes here 

, который, если называется foo=7; echo(foo) (или foo=7; echo('foo'), может быть), печатал бы foo 7. Я понимаю, что это тривиально, если я передаю как переменную, так и ее имя в функцию, но я часто использую такие функции при отладке, и повторение всегда вызывает раздражение.

ответ

7

Не на самом деле решение, но может быть удобно (во всяком случае у вас есть echo('foo') в вопросе):

def echo(**kwargs): 
    for name, value in kwargs.items(): 
     print name, value 

foo = 7 
echo(foo=foo) 

UPDATE: Раствор для echo(foo) с inspect

import inspect 
import re 

def echo(arg): 
    frame = inspect.currentframe() 
    try: 
     context = inspect.getframeinfo(frame.f_back).code_context 
     caller_lines = ''.join([line.strip() for line in context]) 
     m = re.search(r'echo\s*\((.+?)\)$', caller_lines) 
     if m: 
      caller_lines = m.group(1) 
     print caller_lines, arg 
    finally: 
     del frame 

foo = 7 
bar = 3 
baz = 11 
echo(foo) 
echo(foo + bar) 
echo((foo + bar)*baz/(bar+foo)) 

Выход:

foo 7 
foo + bar 10 
(foo + bar)*baz/(bar+foo) 11 

Он имеет наименьший вызов, но он чувствителен к символам новой строки, например.:

echo((foo + bar)* 
     baz/(bar+foo)) 

Напечатает:

baz/(bar+foo)) 11 
+0

Это второе решение чрезвычайно умна. Разрабатывая, как это работает, я нашел дублирующий вопрос [здесь] (http://stackoverflow.com/questions/5503363/can-you-translate-this-debugging-macro-from-c-to-python) - хотя ваше регулярное выражение немного более надежное, чем аналогичный ответ, данный там. – James

+0

отличный ответ, спасибо. не могли бы вы кратко объяснить, как работает регулярное выражение? – Ferguzz

+0

@Ferguzz, он ищет 'echo', затем' \ s * '- 0 или много пробелов,' \ ('- круглые скобки,' (. +?) \) $ '- не-greddy поиск всех символов перед' \) '(круглые скобки) в конце строки (' $ '). Дополнительная информация доступна по адресу http://www.regular-expressions.info/repeat.html. Вы также можете [debug regexps] (http://stackoverflow.com/a/143636/1052325). Попробуйте в интерактивном сеансе Python: 're.compile (r'echo \ s * \ ((. +?) \) $ ', Re.DEBUG)' – reclosedev

3
def echo(x): 
    import inspect 
    print "{0}: {1}".format(x, inspect.stack()[1][0].f_locals[x]) 
y = 123 
echo('y') 
# 'y: 123' 

Смотрите также: https://stackoverflow.com/a/2387854/16361

Обратите внимание, что это может вызвать проблемы с GC:

http://docs.python.org/library/inspect.html#the-interpreter-stack

Он также будет выключаться людей, которые были сожжены баловаться с кадрами, и может оставить плохой вкус во рту. Но это сработает.

3

Вот решение, в котором вы набираете еще немного, чтобы называть его. Он опирается на встроенную функцию locals:

def print_key(dictionary, key): 
    print key, '=', dictionary[key] 


foo = 7 
print_key(locals(), 'foo') 

echo с семантикой вы упоминали также возможно, используя inspect модуль. Однако прочитайте предупреждения в документации инспектора. Это уродливый непереносимый хак (он не работает во всех реализациях Python). Обязательно используйте его только для отладки.

Идея состоит в том, чтобы найти в вызывающей функции locals. Модуль проверки позволяет просто: вызовы представлены объектами фрейма, связанными вместе атрибутом f_back. Доступны локальные и глобальные переменные каждого кадра (есть также встроенные функции, но вряд ли вам придется печатать их).

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

import inspect 

def echo(varname): 
    caller = inspect.currentframe().f_back 
    try: 
     value = caller.f_locals[varname] 
    except KeyError: 
     value = caller.f_globals[varname] 
    print varname, '=', value 
    del caller 

foo = 7 
echo('foo') 
Смежные вопросы