How to Print Original Variable's Name in Python After It Was Returned from a Function

How can you print a variable name in python?

If you insist, here is some horrible inspect-based solution.

import inspect, re

def varname(p):
for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
m = re.search(r'\bvarname\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', line)
if m:
return m.group(1)

if __name__ == '__main__':
spam = 42
print varname(spam)

I hope it will inspire you to reevaluate the problem you have and look for another approach.

How to get the original variable name of variable passed to a function

You can't. It's evaluated before being passed to the function. All you can do is pass it as a string.

How to retrieve a variable's name in python at runtime?

here a basic (maybe weird) function that shows the name of its argument...
the idea is to analyze code and search for the calls to the function (added in the init method it could help to find the instance name, although with a more complex code analysis)

def display(var):
import inspect, re
callingframe = inspect.currentframe().f_back
cntext = "".join(inspect.getframeinfo(callingframe, 5)[3]) #gets 5 lines
m = re.search("display\s+\(\s+(\w+)\s+\)", cntext, re.MULTILINE)
print m.group(1), type(var), var

please note:
getting multiple lines from the calling code helps in case the call was split as in the below example:

display(
my_var
)

but will produce unexpected result on this:

display(first_var)
display(second_var)

If you don't have control on the format of your project you can still improve the code to detect and manage different situations...

Overall I guess a static code analysis could produce a more reliable result, but I'm too lazy to check it now

Python: Print a variable's name and value?

You can just use eval:

def debug(variable):
print variable, '=', repr(eval(variable))

Or more generally (which actually works in the context of the calling function and doesn't break on debug('variable'), but only on CPython):

from __future__ import print_function

import sys

def debug(expression):
frame = sys._getframe(1)

print(expression, '=', repr(eval(expression, frame.f_globals, frame.f_locals)))

And you can do:

>>> x = 1
>>> debug('x + 1')
x + 1 = 2

Returning variable name rather than it's value

min() will find minimum value, so it doesn't care about argument name. But min() can take also iterable as argument.

data = [2, 3, 1]
print(data.index(min(data))

With this you will get position of minimum value.

You can also use more sophisticated approach - store some metadata, like name with data:

data = [('a', 3), ('b', 0), ('c', 1)]

here you have list of tuples, but to find min value you have to let know min what value use for comparison:

minValue = min(list(map(lambda x: x[1], data)))
minPosition = list(map(lambda x: x[1], data)).index(minValue)
print(data[minPosition]) # prints ('b', 0)

Searching for variable name directly is not advised, someone may refactor something later and your code will be broken. It's better to store more information as data.

Getting the name of a variable as a string

TL;DR

Use the Wrapper helper from python-varname:

from varname.helpers import Wrapper

foo = Wrapper(dict())

# foo.name == 'foo'
# foo.value == {}
foo.value['bar'] = 2

For list comprehension part, you can do:

n_jobs = Wrapper(<original_value>) 
users = Wrapper(<original_value>)
queues = Wrapper(<original_value>)
priorities = Wrapper(<original_value>)

list_of_dicts = [n_jobs, users, queues, priorities]
columns = [d.name for d in list_of_dicts]
# ['n_jobs', 'users', 'queues', 'priorities']
# REMEMBER that you have to access the <original_value> by d.value

I am the author of the python-varname package. Please let me know if you have any questions or you can submit issues on Github.

The long answer

Is it even possible?

Yes and No.

We are retrieving the variable names at runtime, so we need a function to be called to enable us to access the previous frames to retrieve the variable names. That's why we need a Wrapper there. In that function, at runtime, we are parsing the source code/AST nodes in the previous frames to get the exact variable name.

However, the source code/AST nodes in the previous frames are not always available, or they could be modified by other environments (e.g: pytest's assert statement). One simple example is that the codes run via exec(). Even though we are still able to retrieve some information from the bytecode, it needs too much effort and it is also error-prone.

How to do it?

First of all, we need to identify which frame the variable is given. It's not always simply the direct previous frame. For example, we may have another wrapper for the function:

from varname import varname

def func():
return varname()

def wrapped():
return func()

x = wrapped()

In the above example, we have to skip the frame inside wrapped to get to the right frame x = wrapped() so that we are able to locate x. The arguments frame and ignore of varname allow us to skip some of these intermediate frames. See more details in the README file and the API docs of the package.

Then we need to parse the AST node to locate where the variable is assigned value (function call) to. It's not always just a simple assignment. Sometimes there could be complex AST nodes, for example, x = [wrapped()]. We need to identify the correct assignment by traversing the AST tree.

How reliable is it?

Once we identify the assignment node, it is reliable.

varname is all depending on executing package to look for the node. The node executing detects is ensured to be the correct one (see also this).

It partially works with environments where other AST magics apply, including pytest, ipython, macropy, birdseye, reticulate with R, etc. Neither executing nor varname is 100% working with those environments.

Do we need a package to do it?

Well, yes and no, again.

If your scenario is simple, the code provided by @juan Isaza or @scohe001 probably is enough for you to work with the case where a variable is defined at the direct previous frame and the AST node is a simple assignment. You just need to go one frame back and retrieve the information there.

However, if the scenario becomes complicated, or we need to adopt different application scenarios, you probably need a package like python-varname, to handle them. These scenarios may include to:

  1. present more friendly messages when the source code is not available or AST nodes are not accessible
  2. skip intermediate frames (allows the function to be wrapped or called in other intermediate frames)
  3. automatically ignores calls from built-in functions or libraries. For example: x = str(func())
  4. retrieve multiple variable names on the left-hand side of the assignment
  5. etc.

How about the f-string?

Like the answer provided by @Aivar Paalberg. It's definitely fast and reliable. However, it's not at runtime, meaning that you have to know it's foo before you print the name out. But with varname, you don't have to know that variable is coming:

from varname import varname

def func():
return varname()

# In external uses
x = func() # 'x'
y = func() # 'y'

Finally

python-varname is not only able to detect the variable name from an assignment, but also:

  • Retrieve variable names directly, using nameof
  • Detect next immediate attribute name, using will
  • Fetch argument names/sources passed to a function using argname

Read more from its documentation.

However, the final word I want to say is that, try to avoid using it whenever you can.

Because you can't make sure that the client code will run in an environment where the source node is available or AST node is accessible. And of course, it costs resources to parse the source code, identify the environment, retrieve the AST nodes and evaluate them when needed.

How to print actual name of variable class type in function?

There are different ways to approach your problem.

The simplest I can fathom is if you can change the class man, make it accept an optional name in its __init__ and store it in the instance. This should look like this:

class man:
def __init__(number, color, name="John Doe"):
self.name = name
# rest of your code here

That way in your function you could just do with:

    return guy1.name

Additionnally, if you want to go an extra step, you could define a __str__ method in your class man so that when you pass it to str() or print(), it shows the name instead:

    # Inside class man
def __str__(self):
return self.name

That way your function could just do:

    return guy1

And when you print the return value of your function it actually prints the name.


If you cannot alter class man, here is an extremely convoluted and costly suggestion, that could probably break depending on context:

import inspect
def competition(guy1, guy2, counter1=0, counter2=0):
guy1_name = ""
guy2_name = ""
for name, value in inspect.stack()[-1].frame.f_locals.items():
if value is guy1:
guy1_name = name
elif value is guy2:
guy2_name = name
if counter1 > counter2:
return guy1_name
elif counter2 > counter2:
return guy1_name
else:
return "Noone"


Related Topics



Leave a reply



Submit