2

I use this very helpful macro when developing in C++:

#define DD(a) std::cout << #a " = [ " << a << " ]" << std::endl;std::cout.flush();

Could you help me implement the same idea in python? I don't know how the #a could be implemented with a python function...

static_rtti
  • 53,760
  • 47
  • 136
  • 192
  • 3
    Totally offtopic: `std::endl;std::cout.flush();` why this? `std::endl` flushs the stream by itself. – Xeo Mar 31 '11 at 16:51
  • Is this in the standard? – static_rtti Mar 31 '11 at 16:54
  • 3
    [Yes](http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2011/n3242.pdf), under 27.7.3.8, [ostream.manip], `endl`: `Effects: Calls os.put(os.widen(’\n’)), then os.flush().` – Xeo Mar 31 '11 at 16:59

6 Answers6

7

As @Andrea Spadaccini and @adirau point out, it is not possible to reliably map values back to Python variable names. You could trawl through all namespaces looking for some variable name that references the given value, but that would be fighting the system and liable to return the wrong variable name.

Much easier it is to just pass the variable name:

import inspect
def pv(name):
    frame,filename,line_number,function_name,lines,index=inspect.getouterframes(
        inspect.currentframe())[1]    
    # print(frame,filename,line_number,function_name,lines,index)
    val=eval(name,frame.f_globals,frame.f_locals)
    print('{0}: {1}'.format(name, val))


a=5
pv('a')

yields:

a: 5
Community
  • 1
  • 1
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
6

You could inspect the stack trace and "parse" it. Since you know the name of your function (dd in this case) it becomes fairly easy to find the call and extract the name of the variable.

    import inspect
    import re

    def dd(value):
        calling_frame_record = inspect.stack()[1]
        frame = inspect.getframeinfo(calling_frame_record[0])
        m = re.search( "dd\((.+)\)", frame.code_context[0])
        if m:
            print "{0} = {1}".format(m.group(1), value)

    def test():
        a = 4
        dd(a)

    test()

Output

a = 4
Rod
  • 52,748
  • 3
  • 38
  • 55
3

I think that this cannot be done.

The debugging macro that you posted works because it is expanded before compilation, during pre-processing, when you know the variable name. It is like you write all those couts by yourself.

Python does not have a pre-processor (AFAIK), there are external tools that do a similar thing (pyp and others), but you can not define a macro with the standard language.

So you should do your trick at run-time. Well, at run-time you don't know the "name" of the variable because the variable is just a reference to an object, when you call a method you call it on the object, not on the "variable". There can be many variables that point to that object, how does the object know which variable was used to call the method?

Andrea Spadaccini
  • 12,378
  • 5
  • 40
  • 54
  • 1
    However, maybe a Python **expert** will come in and show us how to do it.. I'm curious! :) – Andrea Spadaccini Mar 31 '11 at 17:00
  • No. Not even Guido himself could do it. There's no link from an object to the 0..infinity names that refer to it. And there's no (clean and/or advisable) way to circumvent lexical scoping and get into the caller's namespace. –  Mar 31 '11 at 17:28
3

You can't get a variable (well, object)'s name in python. But you can pass the object's name to get its value (kinda the opposite of what you do with that macro)

>>> a=4
>>> locals()['a']
4

EDIT: a detailed explanation may be found here

Community
  • 1
  • 1
user237419
  • 8,829
  • 4
  • 31
  • 38
3
import sys

def DD(expr):
    frame = sys._getframe(1)
    print '%s = %s' % (expr, repr(eval(expr, frame.f_globals, frame.f_locals)))

GLOBAL_VAR = 10

def test():
    local_var = 20
    DD('GLOBAL_VAR + local_var')


>>> test()
GLOBAL_VAR + local_var = 30
hmp
  • 941
  • 3
  • 10
  • 27
2

The Rod solution is perfectly usable. It could be even extended to handle many vars. But you can get close to that with much less magic:

def dd(**kwargs):
    print ", ".join(str(k) + "=" + str(v) for k, v in kwargs.iteritems())

a = 1
dd(a=a,b=3)

output:

a=1, b=3
Community
  • 1
  • 1
pafinde
  • 548
  • 4
  • 6