1

I often write

print "variable_name =", variable_name

in my python scripts, and would prefer to have something like:

name_print(variable_name)

with the same functionality, to save on typing.

Anyone have any suggestions?

This is sort of a duplicate of: How can you print a variable name in python?

But that question got a lot of "you're doing it wrong"-type answers. I think my application is very reasonable, and must be something someone has encountered and solved....

Community
  • 1
  • 1
capybaralet
  • 1,757
  • 3
  • 21
  • 31
  • Python isn't R. If you try to do stuff like this, you can sort of make it work, but you'll make a much messier mess than the one you were trying to clean up. – user2357112 Nov 20 '15 at 23:38
  • @RNar almost as often as I write print "variable_name =", variable_name :P – capybaralet Nov 20 '15 at 23:47
  • gotcha, just making sure. wouldnt want the syntax monsters to come creeping up on you – R Nar Nov 20 '15 at 23:49
  • 1
    Add me to the list of saying *you are doing it wrong*. Have you considered using a debugger such as pdb or [pudb](https://pypi.python.org/pypi/pudb)? – dawg Nov 21 '15 at 00:12

4 Answers4

2

The problem you're going to run into with trying to implement something like name_print(variable_name) is that when the function is called, only its value is passed. The function has no idea of the name of the variable in any scope where it might have relevance. So you'd have to pass the name as a string, then dig the value out of the caller's frame object:

from inspect import currentframe

def name_print(name):
    frame = currentframe().f_back
    locs, globs = frame.f_locals, frame.f_globals
    value = locs[name] if name in locs else globs.get(name, "???")
    print name, "=", value
    del frame

 n = 42
 name_print("n")

You could add additional functionality, such as attribute lookup (e.g. start.line) or even full evaluation, but I'll leave that as an exercise.

The only way to pass a name to a function without having to put it in quotes is to use __getattr__ on an object. So you could write something like this, but it would be very unusual syntax just to avoid using strings.

from inspect import currentframe

class NamePrint(object):

    def __getattribute__(self, name):
        frame = currentframe().f_back
        locs, globs = frame.f_locals, frame.f_globals
        value = locs[name] if name in locs else globs.get(name, "???")
        print name, "=", value
        del frame

n = 42
name_print = NamePrint()
name_print.n
kindall
  • 178,883
  • 35
  • 278
  • 309
  • And even then, the object could be known by many names. Which one is the "right" one? – tdelaney Nov 21 '15 at 00:20
  • Well, by passing in the name, if the value can be accessed by that name, it's good enough. – kindall Nov 21 '15 at 00:21
  • \_\_getattr\_\_ would only work correctly if there was not already an attribute with the same name within that NamePrint object, as \_\_getattr\_\_ is only called if the attribute doesn't exist on the object. Also, you are not protecting against potential memory leaks by calling del on the frame variable within \_\_getattr\_\_ when you are done with it – J2C Nov 21 '15 at 00:37
  • @J2C I'm aware of your first comment which is why there aren't any other attributes on that object. :-) Of course you could use `__getattribute__` there, but there's really no reason to either. I guess it could be slightly more performant. As to the other, there's no reason to `del frame` yourself since exiting the function is going to do exactly that anyway. There's no possibility of a memory leak. – kindall Nov 21 '15 at 01:12
  • @kindall Just thought I would add the attribute comment in case the OP decided to create his own object based on your answer :) As for the memory leak, calling del on the frame is based on the comments in the Python documentation here: https://docs.python.org/2/library/inspect.html#the-interpreter-stack as those inspect frames are inherently cyclic and can sidestep being garbage collected because of this, even when they go out of scope as would normally happen here. The explicit del breaks the cycle and removes any chance of a leak – J2C Nov 21 '15 at 01:51
0

After considering this for the weekend, I have come up with a better answer that allows you to pass the variable, without having to string the variable name first.

def print_variable(variable):
    import inspect
    import re

    frame = inspect.currentframe()
    this_f_name = inspect.getframeinfo(frame).function
    var_name = re.search(r"{}\((\w+)\)".format(this_f_name), inspect.getframeinfo(frame.f_back).code_context[0]).group(1)

    print "{} - {}".format(var_name, variable)

    del frame


def main():
    apples = 100
    print_variable(apples)


if __name__ == '__main__':
    main()

This uses introspection to figure out the the name of the print_variable function, and then examine the source code of the calling context, and extract the content between the brackets of when the function is called.

J2C
  • 497
  • 4
  • 8
-1

Here's a version that uses a dict and the locals() dictionary:

def print_dict(d):
    for k,v in d.items():
        print k, '=', v

print_dict(locals())
Brent Washburne
  • 12,904
  • 4
  • 60
  • 82
-1

As noted by R Nar, there are ways, and hopefully this will help you. You can make use of the inspect module to interrogate frames of reference and the calling stack of you application. I haven't stress tested this code, so can't guarantee the amount of runway you will have with it. Hopefully enough! One that that I can be sure of, is that depending on your specific Python implementation, inspect.currentframe() may not be available. I have tested this on standard CPython, 2.7.10.

You will need to create a function that is accessible where you need it:

def print_variable(variable_name):
    import inspect
    frame = inspect.currentframe()
    try:
        value = frame.f_back.f_locals[variable_name]
        print variable_name, "-", value
    except KeyError:
        print variable_name, "does not exist!"
    finally:
        del frame

The use of the frame and frame.f_back allows you to examine the frame of the calling function (dangerous of course!) and grab the variable's value from the locals of that frame, with f_locals.

As a sidebar, for the reasoning behind the del frame is due to preventing cyclic reference counting that would prevent the garbage collector from deleting the frame object. If you interested in that, take a look here.

This function will rely on you passing the variable you wish to print, by passing the string for its name. Will that get you where you need, or do you want to be able to pass the variable itself (not as a string)?

>>> my_var = '123'
>>> print_variable('my_var')
'123'
J2C
  • 497
  • 4
  • 8