130

Say I have a variable named choice it is equal to 2. How would I access the name of the variable? Something equivalent to

In [53]: namestr(choice)
Out[53]: 'choice'

for use in making a dictionary. There's a good way to do this and I'm just missing it.

EDIT:

The reason to do this is thus. I am running some data analysis stuff where I call the program with multiple parameters that I would like to tweak, or not tweak, at runtime. I read in the parameters I used in the last run from a .config file formated as

filename
no_sig_resonance.dat

mass_peak
700

choice
1,2,3

When prompted for values, the previously used is displayed and an empty string input will use the previously used value.

My question comes about because when it comes to writing the dictionary that these values have been scanned into. If a parameter is needed I run get_param which accesses the file and finds the parameter.

I think I will avoid the problem all together by reading the .config file once and producing a dictionary from that. I avoided that originally for... reasons I no longer remember. Perfect situation to update my code!

Constantin
  • 27,478
  • 10
  • 60
  • 79
physicsmichael
  • 4,793
  • 11
  • 35
  • 54
  • 1
    http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables – jfs Feb 26 '09 at 22:33
  • The link provides some pictures that in 2 minutes will teach you what are names in Python. – jfs Feb 26 '09 at 22:34
  • @dF: This question is different. It is not about enums. – jfs Feb 26 '09 at 22:57
  • @Sebastian, I've had the Idiomatic Python on my toolbar for a long time. Those pictures taught me how Python names variables the first time I read it. I understand how it does it and how it may be difficult to do what I ask because of it, but is there a good way to do it I ask. – physicsmichael Feb 27 '09 at 00:11
  • ConfigParser (from @Alabaster Codify's answer) allows you to produce a dictionary from a config file (in .ini format) in one line. – jfs Feb 27 '09 at 00:18
  • 1
    also related: http://stackoverflow.com/questions/932818/retrieving-a-variables-name-in-python-at-runtime – sdaau Dec 17 '13 at 00:11
  • 18
    There is little worse on Stack Overflow than people questioning the existence of a question itself. – rtphokie Jan 17 '20 at 22:28
  • 2
    Finally, a perfectly valid solution with Python 3.8 f-string ```=``` syntax : https://stackoverflow.com/a/57225950/4080671 – M. Heuer Oct 17 '20 at 19:32

8 Answers8

150

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.

Constantin
  • 27,478
  • 10
  • 60
  • 79
  • 1
    There is related use case for [`inspect.currentframe().f_back.f_locals`](http://stackoverflow.com/q/13312240/4279) – jfs Nov 27 '12 at 00:10
  • 191
    Sometimes a night of debugging makes us do things we're not proud of. – Jake Feb 06 '14 at 20:40
  • 15
    upvoted for literal LOL – Enteleform Apr 05 '16 at 09:19
  • 9
    I like how Jake's comment comes 5 years after the question and yet it still has 70+ up votes of people that, like me, reluctantly searched how to do this shameless thing in a moment of desperation – M. Lautaro Jan 09 '20 at 20:07
  • It doesn't work with list comprehension `a,b=1,2; print([varname(x) for x in [a,b]])` gives `['x', 'x']` – shahar_m Apr 18 '21 at 11:13
  • Yes it does. When it is passed to the function `varname` the variable name is indeed `x`. – ticster Jan 02 '23 at 21:44
112

To answer your original question:

def namestr(obj, namespace):
    return [name for name in namespace if namespace[name] is obj]

Example:

>>> a = 'some var'
>>> namestr(a, globals())
['a']

As @rbright already pointed out whatever you do there are probably better ways to do it.

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 5
    This fails if there is more than one variable name referring to the same value. – Greg Hewgill Feb 26 '09 at 23:33
  • 2
    @Greg Hewgill: You might have noticed that `namestr` returns *list*. It is a hint that there could be more than one name. – jfs Feb 26 '09 at 23:37
  • @Greg Hewgill: It doesn't matter in this case (due to strings are immutable) but generally there is a difference between object's value and its identity. Therefore it is better to say that a name refers to an object (not its value). Mutable objects may change their values, but not identity id(). – jfs Feb 26 '09 at 23:43
  • Ok, I seem to have missed the list. Fair enough, but integers don't always share representation, see: http://stackoverflow.com/questions/306313/python-is-operator-behaves-unexpectedly-with-integers – Greg Hewgill Feb 27 '09 at 00:01
  • 1
    I'd say most integers don't share their representations. – jfs Feb 27 '09 at 00:13
  • 3
    I like the flow of your answer code, but I may be working with integers that may be repeated several times in the code... *sigh* – physicsmichael Feb 27 '09 at 00:15
  • @J.F.Sebastian *I'd say most integers don't share their representations* I'm pretty sure there is the same amount of each of these two kinds :) Both sets are countable. – Piotr Dobrogost Jan 26 '12 at 11:45
  • 2
    @Piotr Dobrogost: *"share their representions"* refers to CPython optimization that caches small integers therefore one of the sets is not countable it is finite. – jfs Jan 26 '12 at 18:23
  • I like this solution the best, but did not like how '_' or '_70' would also show up. Unless you are searching fro private objects this provides a bit cleaner output. ``` def var_name(obj, namespace=globals()): return [name for name in namespace if namespace[name] is obj and name[0] != '\_'][0] ``` – Waylon Walker Jun 07 '17 at 18:37
17

If you are trying to do this, it means you are doing something wrong. Consider using a dict instead.

def show_val(vals, name):
    print "Name:", name, "val:", vals[name]

vals = {'a': 1, 'b': 2}
show_val(vals, 'b')

Output:

Name: b val: 2
recursive
  • 83,943
  • 34
  • 151
  • 241
10

Rather than ask for details to a specific solution, I recommend describing the problem you face; I think you'll get better answers. I say this since there's almost certainly a better way to do whatever it is you're trying to do. Accessing variable names in this way is not commonly needed to solve problems in any language.

That said, all of your variable names are already in dictionaries which are accessible through the built-in functions locals and globals. Use the correct one for the scope you are inspecting.

One of the few common idioms for inspecting these dictionaries is for easy string interpolation:

>>> first = 'John'
>>> last = 'Doe'
>>> print '%(first)s %(last)s' % globals()
John Doe

This sort of thing tends to be a bit more readable than the alternatives even though it requires inspecting variables by name.

Ryan Bright
  • 3,495
  • 21
  • 20
  • I want to try to and print 'first' and 'last', the values that I call the references, not what they are equal to. – physicsmichael Feb 26 '09 at 22:52
  • I know, I was just letting you know one reason people commonly look at those dictionaries at all. Usually you shouldn't need to. Unless you want to give us more detail, just dig around in them for what you're looking for. E.g., globals().keys() – Ryan Bright Feb 26 '09 at 23:04
9

You can't, as there are no variables in Python but only names.

For example:

> a = [1,2,3]
> b = a
> a is b
True

Which of those two is now the correct variable? There's no difference between a and b.

There's been a similar question before.

Community
  • 1
  • 1
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 42
    The difference between a and b is the name. – Kenan Banks Feb 26 '09 at 22:34
  • 1
    gs is right. a and b refer to the same object, so a function receiving this object has no way to know which name was used to refer to the object in the function call. – dF. Feb 26 '09 at 22:42
  • 3
    He's kinda right, in that there is no way for you to write a function in Python to differentiate. Still, there's no reason there couldn't be a built-in function that accomplishes this. – Kenan Banks Feb 26 '09 at 22:49
  • dis.dis() returns some names (not always). – jfs Feb 26 '09 at 23:34
  • Therefore It is sometimes possible to distinguish between `a` and `b` even they're referring to the same object. Python scopes are static. Namespaces are dynamic. – jfs Feb 26 '09 at 23:36
  • It means that e.g. when a function returns there might be no local namespace of that function left but you still may inspect the function code object to find what names are used inside. – jfs Feb 26 '09 at 23:54
  • 22
    "There are no variables in Python" Silly misinformation. You mean, "variables in Python work differently than in C." – Ned Batchelder Dec 08 '12 at 23:27
4

With eager evaluation, variables essentially turn into their values any time you look at them (to paraphrase). That said, Python does have built-in namespaces. For example, locals() will return a dictionary mapping a function's variables' names to their values, and globals() does the same for a module. Thus:

for name, value in globals().items():
    if value is unknown_variable:
        ... do something with name

Note that you don't need to import anything to be able to access locals() and globals().

Also, if there are multiple aliases for a value, iterating through a namespace only finds the first one.

Nikhil
  • 5,705
  • 1
  • 32
  • 30
4

Will something like this work for you?

>>> def namestr(**kwargs):
...     for k,v in kwargs.items():
...       print "%s = %s" % (k, repr(v))
...
>>> namestr(a=1, b=2)
a = 1
b = 2

And in your example:

>>> choice = {'key': 24; 'data': None}
>>> namestr(choice=choice)
choice = {'data': None, 'key': 24}
>>> printvars(**globals())
__builtins__ = <module '__builtin__' (built-in)>
__name__ = '__main__'
__doc__ = None
namestr = <function namestr at 0xb7d8ec34>
choice = {'data': None, 'key': 24}
myroslav
  • 3,703
  • 23
  • 29
3

For the revised question of how to read in configuration parameters, I'd strongly recommend saving yourself some time and effort and use ConfigParser or (my preferred tool) ConfigObj.

They can do everything you need, they're easy to use, and someone else has already worried about how to get them to work properly!

James Brady
  • 27,032
  • 8
  • 51
  • 59