Convert Variable Name to String?

130,388

Solution 1

TL;DR: Not possible. See 'conclusion' at the end.


There is an usage scenario where you might need this. I'm not implying there are not better ways or achieving the same functionality.

This would be useful in order to 'dump' an arbitrary list of dictionaries in case of error, in debug modes and other similar situations.

What would be needed, is the reverse of the eval() function:

get_indentifier_name_missing_function()

which would take an identifier name ('variable','dictionary',etc) as an argument, and return a string containing the identifier’s name.


Consider the following current state of affairs:

random_function(argument_data)

If one is passing an identifier name ('function','variable','dictionary',etc) argument_data to a random_function() (another identifier name), one actually passes an identifier (e.g.: <argument_data object at 0xb1ce10>) to another identifier (e.g.: <function random_function at 0xafff78>):

<function random_function at 0xafff78>(<argument_data object at 0xb1ce10>)

From my understanding, only the memory address is passed to the function:

<function at 0xafff78>(<object at 0xb1ce10>)

Therefore, one would need to pass a string as an argument to random_function() in order for that function to have the argument's identifier name:

random_function('argument_data')

Inside the random_function()

def random_function(first_argument):

, one would use the already supplied string 'argument_data' to:

  1. serve as an 'identifier name' (to display, log, string split/concat, whatever)

  2. feed the eval() function in order to get a reference to the actual identifier, and therefore, a reference to the real data:

    print("Currently working on", first_argument)
    some_internal_var = eval(first_argument)
    print("here comes the data: " + str(some_internal_var))
    

Unfortunately, this doesn't work in all cases. It only works if the random_function() can resolve the 'argument_data' string to an actual identifier. I.e. If argument_data identifier name is available in the random_function()'s namespace.

This isn't always the case:

# main1.py
import some_module1

argument_data = 'my data'

some_module1.random_function('argument_data')


# some_module1.py
def random_function(first_argument):
    print("Currently working on", first_argument)
    some_internal_var = eval(first_argument)
    print("here comes the data: " + str(some_internal_var))
######

Expected results would be:

Currently working on: argument_data
here comes the data: my data

Because argument_data identifier name is not available in the random_function()'s namespace, this would yield instead:

Currently working on argument_data
Traceback (most recent call last):
  File "~/main1.py", line 6, in <module>
    some_module1.random_function('argument_data')
  File "~/some_module1.py", line 4, in random_function
    some_internal_var = eval(first_argument)
  File "<string>", line 1, in <module>
NameError: name 'argument_data' is not defined

Now, consider the hypotetical usage of a get_indentifier_name_missing_function() which would behave as described above.

Here's a dummy Python 3.0 code: .

# main2.py
import some_module2
some_dictionary_1       = { 'definition_1':'text_1',
                            'definition_2':'text_2',
                            'etc':'etc.' }
some_other_dictionary_2 = { 'key_3':'value_3',
                            'key_4':'value_4', 
                            'etc':'etc.' }
#
# more such stuff
#
some_other_dictionary_n = { 'random_n':'random_n',
                            'etc':'etc.' }

for each_one_of_my_dictionaries in ( some_dictionary_1,
                                     some_other_dictionary_2,
                                     ...,
                                     some_other_dictionary_n ):
    some_module2.some_function(each_one_of_my_dictionaries)


# some_module2.py
def some_function(a_dictionary_object):
    for _key, _value in a_dictionary_object.items():
        print( get_indentifier_name_missing_function(a_dictionary_object)    +
               "    " +
               str(_key) +
               "  =  " +
               str(_value) )
######

Expected results would be:

some_dictionary_1    definition_1  =  text_1
some_dictionary_1    definition_2  =  text_2
some_dictionary_1    etc  =  etc.
some_other_dictionary_2    key_3  =  value_3
some_other_dictionary_2    key_4  =  value_4
some_other_dictionary_2    etc  =  etc.
......
......
......
some_other_dictionary_n    random_n  =  random_n
some_other_dictionary_n    etc  =  etc.

Unfortunately, get_indentifier_name_missing_function() would not see the 'original' identifier names (some_dictionary_,some_other_dictionary_2,some_other_dictionary_n). It would only see the a_dictionary_object identifier name.

Therefore the real result would rather be:

a_dictionary_object    definition_1  =  text_1
a_dictionary_object    definition_2  =  text_2
a_dictionary_object    etc  =  etc.
a_dictionary_object    key_3  =  value_3
a_dictionary_object    key_4  =  value_4
a_dictionary_object    etc  =  etc.
......
......
......
a_dictionary_object    random_n  =  random_n
a_dictionary_object    etc  =  etc.

So, the reverse of the eval() function won't be that useful in this case.


Currently, one would need to do this:

# main2.py same as above, except:

    for each_one_of_my_dictionaries_names in ( 'some_dictionary_1',
                                               'some_other_dictionary_2',
                                               '...',
                                               'some_other_dictionary_n' ):
        some_module2.some_function( { each_one_of_my_dictionaries_names :
                                     eval(each_one_of_my_dictionaries_names) } )
    
    
    # some_module2.py
    def some_function(a_dictionary_name_object_container):
        for _dictionary_name, _dictionary_object in a_dictionary_name_object_container.items():
            for _key, _value in _dictionary_object.items():
                print( str(_dictionary_name) +
                       "    " +
                       str(_key) +
                       "  =  " +
                       str(_value) )
    ######

In conclusion:

  • Python passes only memory addresses as arguments to functions.
  • Strings representing the name of an identifier, can only be referenced back to the actual identifier by the eval() function if the name identifier is available in the current namespace.
  • A hypothetical reverse of the eval() function, would not be useful in cases where the identifier name is not 'seen' directly by the calling code. E.g. inside any called function.
  • Currently one needs to pass to a function:
    1. the string representing the identifier name
    2. the actual identifier (memory address)

This can be achieved by passing both the 'string' and eval('string') to the called function at the same time. I think this is the most 'general' way of solving this egg-chicken problem across arbitrary functions, modules, namespaces, without using corner-case solutions. The only downside is the use of the eval() function which may easily lead to unsecured code. Care must be taken to not feed the eval() function with just about anything, especially unfiltered external-input data.

Solution 2

Totally possible with the python-varname package (python3):

from varname import nameof

s = 'Hey!'

print (nameof(s))

Output:

s

Install:

pip3 install varname

Or get the package here:

https://github.com/pwwang/python-varname

Solution 3

I searched for this question because I wanted a Python program to print assignment statements for some of the variables in the program. For example, it might print "foo = 3, bar = 21, baz = 432". The print function would need the variable names in string form. I could have provided my code with the strings "foo","bar", and "baz", but that felt like repeating myself. After reading the previous answers, I developed the solution below.

The globals() function behaves like a dict with variable names (in the form of strings) as keys. I wanted to retrieve from globals() the key corresponding to the value of each variable. The method globals().items() returns a list of tuples; in each tuple the first item is the variable name (as a string) and the second is the variable value. My variablename() function searches through that list to find the variable name(s) that corresponds to the value of the variable whose name I need in string form.

The function itertools.ifilter() does the search by testing each tuple in the globals().items() list with the function lambda x: var is globals()[x[0]]. In that function x is the tuple being tested; x[0] is the variable name (as a string) and x[1] is the value. The lambda function tests whether the value of the tested variable is the same as the value of the variable passed to variablename(). In fact, by using the is operator, the lambda function tests whether the name of the tested variable is bound to the exact same object as the variable passed to variablename(). If so, the tuple passes the test and is returned by ifilter().

The itertools.ifilter() function actually returns an iterator which doesn't return any results until it is called properly. To get it called properly, I put it inside a list comprehension [tpl[0] for tpl ... globals().items())]. The list comprehension saves only the variable name tpl[0], ignoring the variable value. The list that is created contains one or more names (as strings) that are bound to the value of the variable passed to variablename().

In the uses of variablename() shown below, the desired string is returned as an element in a list. In many cases, it will be the only item in the list. If another variable name is assigned the same value, however, the list will be longer.

>>> def variablename(var):
...     import itertools
...     return [tpl[0] for tpl in 
...     itertools.ifilter(lambda x: var is x[1], globals().items())]
... 
>>> var = {}
>>> variablename(var)
['var']
>>> something_else = 3
>>> variablename(something_else)
['something_else']
>>> yet_another = 3
>>> variablename(something_else)
['yet_another', 'something_else']

Solution 4

as long as it's a variable and not a second class, this here works for me:

def print_var_name(variable):
 for name in globals():
     if eval(name) == variable:
        print name
foo = 123
print_var_name(foo)
>>>foo

this happens for class members:

class xyz:
     def __init__(self):
         pass
member = xyz()
print_var_name(member)
>>>member

ans this for classes (as example):

abc = xyz
print_var_name(abc)
>>>abc
>>>xyz

So for classes it gives you the name AND the properteries

Solution 5

This is not possible.

In Python, there really isn't any such thing as a "variable". What Python really has are "names" which can have objects bound to them. It makes no difference to the object what names, if any, it might be bound to. It might be bound to dozens of different names, or none.

Consider this example:

foo = 1
bar = 1
baz = 1

Now, suppose you have the integer object with value 1, and you want to work backwards and find its name. What would you print? Three different names have that object bound to them, and all are equally valid.

In Python, a name is a way to access an object, so there is no way to work with names directly. There might be some clever way to hack the Python bytecodes or something to get the value of the name, but that is at best a parlor trick.

If you know you want print foo to print "foo", you might as well just execute print "foo" in the first place.

EDIT: I have changed the wording slightly to make this more clear. Also, here is an even better example:

foo = 1
bar = foo
baz = foo

In practice, Python reuses the same object for integers with common values like 0 or 1, so the first example should bind the same object to all three names. But this example is crystal clear: the same object is bound to foo, bar, and baz.

Share:
130,388
Brandon Pelfrey
Author by

Brandon Pelfrey

Programmer, student, and musician. I do real-time physics simulations for games. Fluids, particle methods, multi-grid methods.

Updated on October 10, 2021

Comments

  • Brandon Pelfrey
    Brandon Pelfrey over 2 years

    I would like to convert a python variable name into the string equivalent as shown. Any ideas how?

    var = {}
    print ???  # Would like to see 'var'
    something_else = 3
    print ???  # Would print 'something_else'
    
  • John La Rooy
    John La Rooy over 14 years
    objects don't have names, names have objects
  • Todd
    Todd over 14 years
    There is a way to work with these names, the globals() dict, but it's not clear how to use that to do what the OP asked.
  • SilentGhost
    SilentGhost over 14 years
    of course, it is possible. and quite trivial at that (it solves the OP's problem, anyway). it's just something no one should want to do.
  • Todd
    Todd over 14 years
    Chris, your comment doesn't seem to relate to my answer or the original question. Who are you scolding and why? You don't know if he's writing production code or just experimenting with language features for his own education.
  • Brandon Pelfrey
    Brandon Pelfrey over 14 years
    This is pretty cool, but quite as automatic as I would like. Thanks for the tip =]
  • joachim stephen
    joachim stephen about 11 years
    Please edit your post for formatting issues. It is difficult to understand what you are saying.
  • WeizhongTu
    WeizhongTu over 8 years
    I think in django models, fields are attributes of one class or instance, getting attribute names is not the same thing with converting variable name to string.
  • user5920660
    user5920660 over 6 years
    It is a real pleasure to read a post that answers the question without ranting about why the question is being asked.
  • Brendan
    Brendan almost 6 years
    This is an outstanding write-up, and the kind of high-quality content I come here for.
  • razdi
    razdi over 4 years
    You are looking up the globals dict. This exact thing has been done in a previous answer without creating extra variables and using itertools. While the answer is good, I think since a similar solution has been provided, you should upvote that and avoid repetition.
  • Seymour
    Seymour over 4 years
    There is something not right in Python if to obtain the name of an object we need a 5 pages answer on StackOverflow. :/
  • Steve
    Steve about 4 years
    This should be a comment, not an answer.
  • Nimitz14
    Nimitz14 over 3 years
    A tl;dr would be nice.
  • Petru Zaharia
    Petru Zaharia over 3 years
    @Nimitz14 Included TL;DR. Feel free to propose edits, corrections.
  • MatrixTheatrics
    MatrixTheatrics over 3 years
    "There is absolutely no reason to ever do what you describe" - We have a local all-knowing entity here. There are actually some good usecases for this.
  • Robyc
    Robyc over 3 years
    the TLDR states that it's not possible to achieve what has been asked by the OP. Yet, the answer of @Alice and other similar answers in which the use of locals() or globals() is suggested achieve exactly that. So this TLDR is misleading and this should not be the accepted answer.
  • Robyc
    Robyc over 3 years
    you say it's not possible. Yet is possible. See other answers. @SilentGhost: may you clarify why "no one should want to do it"? I guess it depends on the application. What's your argument?
  • steveha
    steveha over 3 years
    @Robyc You say "Yet is possible." Okay. I gave examples binding foo bar and baz all to 1. Then what's "the name" of 1? If you look through all the globals you could return a list of variable names: ['foo', 'bar', 'baz'] (in some random order). Okay. What is the use of this list? Is one of the names the "true" name of 1? (No.) Those of us who are saying "it" is not possible all agree that you could look through globals and find all the names; we just don't think that's a useful thing to do. As I said: an object "might be bound to dozens of different names, or none."
  • steveha
    steveha over 3 years
    @Robyc You also said "I guess it depends on the application." Okay. Please tell me an example of some case where it would be useful to have the ability to look through globals and find all the variable names that have a given object reference bound to them. I'm not aware of one, but if you have one, I'm quite interested; please share it with us.
  • Robyc
    Robyc over 3 years
    I defines a bunch of variable "names" (they can point to the same value, it doesn't matter) that are used to do some processing. Then I want to save the results of my processing writing in the filename the "name" of the variable I used for that specific result, so to keep track of what is what. E.g. var1=10 ==> result1 = some_processing(var1) ==> out_file_1 = 'some_result_using_var1.png'. Of course you can do that with a dictionary, for example: var1 = {"var1" : 10} and is maybe a more pythonic way to do it.
  • steveha
    steveha over 3 years
    @Robyc If you are working with a bunch of names, it is definitely best practice to keep a list or dictionary of the names and use that to look them up again, and not to try to look through the globals and hope to recover the names later.
  • steveha
    steveha over 3 years
    Note that it's possible to have the same value bound to multiple names. In this case, the above code will print multiple names. There is no way to know which name is the "correct" one.
  • Robyc
    Robyc over 3 years
    Agreed. After trying the "globals()" way it became apparent that having a dictionary is a much better way to achieve my goal (as I also stated in my prev comment). Again, and sorry if I'm picky, you shouldn't write that "it's not possible" if it actually is, hence my critique. You can write "it's highly not advisable in all cases I can think of" :)
  • steveha
    steveha over 3 years
    @Robyc The original question was to recover the name of a variable. That really is "not possible": you can't start with a value and find "the" name. You can go through globals and make a list of all names that currently have that value bound to them, and if there is only one name in the list then I guess you found "the" name; but the basic premise of the question was flawed and I don't think it was wrong to try to educate the person who asked the question on how things actually work. A value could be bound to dozens of names, or none.
  • Erik Aronesty
    Erik Aronesty over 3 years
    use is variable not == variable ... safer
  • Erik Aronesty
    Erik Aronesty over 3 years
    i created a varname function, below. works for methods too.
  • Erik Aronesty
    Erik Aronesty over 3 years
    @seymour i wrote a varname function below that actually answers the question. imo: this is a missing feature from python. i explained why. this accepted answer is miselading.
  • Codemeister
    Codemeister about 3 years
    There are a number of languages that provide that functionality - even C/C++ compiled to machine-language can via defines; Python boasts as easy to use entry point for programming but lacks such a popular tool for people to learn and see what their program is doing; Like C/C++, a "compile-time" function could do the job perfectly but it seems all suggested methods of trying to locate a variable name in globals() or etc is only partially successful and cannot work 100% of the time. Matching is done on the objects that multiple variables could reference.
  • mah65
    mah65 about 3 years
    This is wrong. I want to check the name of a variable in a loop! So, I cannot type it and it is not pointless!
  • JC_CL
    JC_CL almost 3 years
    Unfortunately, this doesn't seem to work in python 3 anymore. Changing print name to print(name) is quite obvious, but it still errors out. @Erik Aronesty's fix gets it to work, but the issue @steveha points out, still remains.
  • Sven Eberth
    Sven Eberth almost 3 years
    This package is already mentioned here. (Btw: the ! is incorrect before pip).
  • Gabriel Ceron Viveros
    Gabriel Ceron Viveros almost 3 years
    Ya I noticed it was already mentioned after posting. You are right about the ´!´, I edited it. Was like that because I was on a notebook, only use ´!´ if you are on Jupyter.
  • Arend
    Arend over 2 years
    Thanks @restrepo, this was exactly what I needed to create a standard save_df_to_file() function. For this, I made some small changes to your tostr() function: ` def variabletostr(**df): variablename = list(df.keys())[0] return variablename variabletostr(df=0) ` FYI: I added this code as a new post in this thread
  • Peter Cibulskis
    Peter Cibulskis about 2 years
    sth didn't understand the question. Other languages support functions like varname. Python currently has no method to do this. Every who uses other languages uses this functionality all of the time. Idiomatic python is one thing, ignoring the question is another thing.