67

I have enum and use the variables like myEnum.SomeNameA, myEnum.SomeNameB, etc. When I return one of these variables from a function, can I print their names (such as myEnum.SomeNameA) instead of the value they returned?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985

14 Answers14

38

Short answer: no.

Long answer: this is possible with some ugly hacks using traceback, inspect and the like, but it's generally probably not recommended for production code. For example see:

Perhaps you can use a workaround to translate the value back to a name/representational string. If you post some more sample code and details about what you're wanting this for maybe we can provide more in-depth assistance.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
Jay
  • 41,768
  • 14
  • 66
  • 83
  • 1
    How would you define a function that prints out both the variable's value AND its name? Say, for example, the days in a month, by entering a month's name, it should spit back "month_name has x days". There must be a way, unless I have to ask users to input the chosen month (in this example) twice! –  Sep 05 '16 at 16:37
  • 2
    `a = 1; b = a`. What is the name of `1`? – kindall Jan 29 '18 at 18:18
30

There is no such thing as a unique or original variable name http://www.amk.ca/quotations/python-quotes/page-8

The same way as you get the name of that cat you found on your porch: the cat (object) itself cannot tell you its name, and it doesn't really care -- so the only way to find out what it's called is to ask all your neighbours (namespaces) if it's their cat (object)...

....and don't be surprised if you'll find that it's known by many names, or no name at all!

Fredrik Lundh, 3 Nov 2000, in answer to the question "How can I get the name of a variable from C++ when I have the PyObject*?"

Johan Buret
  • 2,614
  • 24
  • 32
25

To add to @Jay's answer, some concepts...

Python's "variables" are simply references to values. Each value occupies a given memory location (see id()):

>>> id(1)
10052552

>>> sys.getrefcount(1)
569

From the above, you may notice that the value 1 is present at the memory location 10052552. It is referred to 569 times in this instance of the interpreter.

>>> MYVAR = 1
>>> sys.getrefcount(1)
570

Now, see that because yet another name is bound to this value, the reference count went up by one.

Based on these facts, it is not realistic/possible to tell what single variable name is pointing to a value.

I think the best way to address your issue is to add a mapping and function to your enum reference back to a string name:

myEnum.get_name(myEnum.SomeNameA) 
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
gahooa
  • 131,293
  • 12
  • 98
  • 101
10

Python 3.8 added a new and probably a LITTLE better solution to this:

my_nice_variable_name = 'test'
print(f'{my_nice_variable_name=}')

# Output:
# my_nice_variable_name='test'

Here you could work with the string output. Still not advisable but maybe a little better than other ways of doing it.

Kevin Müller
  • 722
  • 6
  • 18
8

Yes.

In your case with enums, it's easy:

>>> from enum import Enum
>>> class Type(Enum):
...   AB, SBS, INTERLACED, SPLIT = range(4)
...
>>> Type.AB
<Type.AB: 0>
>>> print(Type.AB)
Type.AB
>>> Type.AB.name
'AB'
>>> Type.AB.value
0
>>> Type(0)
<Type.AB: 0>

In general, it could be ambiguous:

>>> def varname(v): d = globals(); return [k for k in d if d[k] == v]
...
>>> def varnameis(v): d = globals(); return [k for k in d if d[k] is v]
...
>>> foo = {'a':'aap'}
>>> bar = {'a':'aap'}
>>> varname(foo)
['foo', 'bar']
>>> varnameis(foo)
['foo']

Or even misleading:

>>> foo = "A"; bar = "A"
>>> varnameis("A")
['foo', 'bar']
>>> foo = "An immutable value."; bar = "An immutable value."
>>> varnameis("An immutable value.")
[]
>>> foo = "An immutable value."; bar = "An immutable value."; varnameis("An immutable value.")
['foo', 'bar']
>>> import sys; sys.version
'3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 02:47:15) [MSC v.1900 32 bit (Intel)]'

To search in any scope, use this:

>>> def varname(v, scope=None): d = globals() if not scope else vars(scope); return [k for k in d if d[k] == v]
...
>>> import time
>>> time.timezone
-3600
>>> varname(-3600)
[]
>>> varname(-3600, time)
['timezone']

To avoid ambiguity, wrap your name with the data.

Cees Timmerman
  • 17,623
  • 11
  • 91
  • 124
  • How would you do this, if you import something and it is defined as `ANAME=1`, and you want to print that as `"The Name is: ANAME"`? – not2qubit Oct 19 '18 at 12:52
  • 1
    If it's in the global scope, `varnameis(1)` should do. – Cees Timmerman Oct 23 '18 at 08:00
  • Yeah, the problem I think, is that there's also some `BNAME=1` so it's not uniqe. Just as you demonstrated above. Thus I was forced to compare this to an array of acceptable *names*. – not2qubit Oct 23 '18 at 08:44
6

YES

import inspect
        
def printVariableName(abc):
    newvar=inspect.stack()[1][4][0]
    print(newvar.split("(")[1].split(")")[0])

noob=10
printVariableName(noob)

Output:

noob
Community
  • 1
  • 1
ProNoob
  • 61
  • 1
  • 5
5

Just use the text you want to print as the value of the enum, as in

class MyEnum (object):
    valueA = "valueA"
    valueB = "valueB"

comparing strings for identity is almost as efficient in Python as is comparing integer values (this is due to the fact the strings are immutable as have a hash value)

Of course there are easier ways to create the enum in the first place:

class Enum (object):
    def __init__(self, *values):
        for v in values:
            self.__dict__[v] = v

Then, create you enumeration like this:

MyEnum = Enum("valueA", "valueB")

ans access the same way as above:

MyEnum.valueA
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Ber
  • 40,356
  • 16
  • 72
  • 88
  • +1: Nice workaround for enums. Generally, they're useless, but this is a tidy solution. – S.Lott Feb 13 '09 at 11:24
  • This is totally un-pythonic. What could be the advantage of using this class instead of a dict. (not, downvoting though. legitimate answer) – muhuk Feb 18 '09 at 21:16
  • @muhuk: What use is a dict to emulate enum behaviour? The purpose of the Enum() class in my example is to provide access to the values as named attributes vs. literals. I cannot see why this is un-pythonic; I agree that Enums should be avoided at all, as you can see in by other answer to this topic. – Ber Feb 19 '09 at 15:49
  • Enums presumably should be immutable therefore all attributes must be set in __new__(). – jfs Feb 27 '09 at 09:04
  • But what if the wanted assigned value is different, like an int? –  Sep 05 '16 at 16:38
3

There are two answers to this question: Yes and No.

Can you do it?  -- Yes (in most cases)
Is it worth it? -- No  (in most cases)

How to do it:

It depends on an implementation of enums. For example:

class Enum:
    A = 1
    B = 2

It doesn't matter whether there are 100 names refers to an integer whose name you'd like to find if you know enums class object and all they're names (or at least a unique prefix) in that class.

For the example above as @batbrat suggested you can inspect 'Enum.__dict__' using namestr():

 >>> getEnum = lambda: Enum.A
 >>> namestr(getEnum(), Enum.__dict__)
 >>> ['A']

In this case you even don't have to know all enum names. If enums implemented differently then you might need to use a different hack. There will be some solution in most cases e.g., see @Jay's answer. But it doesn't matter because..

Is it worth it?

  • Some enum implementations may require ugly, complex and in the end unreliable hacks to implement namestr().

  • Enum class can return answer by itself (see @David's, @Ber's, @gahooa's answers).

  • As @Ber pointed out there is no builtin Enum and switch statement in Python (see PEP-0275, PEP-3103). It is worth investigating solutions without enums.

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
2

You could store the canonical name as an attribute of the instance, and then assign it to a variable with the same name. This might work:

class MyEnum(object):
    def __new__(cls, name):
        try:
            return getattr(MyEnum, name)
        except AttributeError:
            e = super(MyEnum, cls).__new__(cls)
            e.name = name
            setattr(MyEnum, name, e)
            return e

Regardless, it's not a particularly "Pythonic" thing to do.

David Z
  • 128,184
  • 27
  • 255
  • 279
1

On second thought:

Since Python does not provide native Enum types, you should not ask for one, but instead use other, more powerful construct to build your program. Otherwise, the next step will invariably be "Why does Python not have a switch ...: statement, and how do I best emulate it?"

Since Enums are often used to define some kind of state, a much better approach is this: Create a base class that define all the abstract properties, attributes and methods belonging to a state. Then, for each state, derive a sub class that implements the specific behavior of this state. You can then pass around these classes (or maybe instances thereof) to handle the state and its behaviour.

If you use classes instead of instances (the Python way of a "singleton"), you can simply check for any given state (not that it should be necessary) by if current_state is StateA: (note the is instead of ==) with no performance penalty over comparing integer values.

And of course, you can define a name attribute and a __str__() method to access and print the state's name.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Ber
  • 40,356
  • 16
  • 72
  • 88
0

As far as I know, that will require some introspection. You can try using the inspect module.

There are a few simple things you may want to try before that:

  1. This is not the exact code that should be used. You will have to retrieve the variable name from __dict__ before printing.
    print myEnum.__dict__
            
  2. If you want to print the variable name from inside the function, you can try the functions - vars(), locals() and globals().
    Edit:
    I just noticed that this is not what you want. In that case adding a dict and a function may work
  3. You may want to print the __dict__ of the class of your enums.

All that said, there aren't standard enumerations in Python. It would help to know how you are creating them.

On second thoughts, you can maintain your variables as a dictionary in the enum, keyed by variable name and provide a method of the enumeration to find the right variable and print its name. This solution (keeping a dict) is bad because variable values aren't necessarily unique.

Edit:
The problem is not trivial, so you may want to use a tried and tested solution. IMHO, you would be better off avoiding the situation if you can.

batbrat
  • 5,155
  • 3
  • 32
  • 38
0

Erlang has a concept called "atoms" -- they are similar to string constants or enumerations. Consider using a string constant as the value of your enum -- the same as the name of the enum.

gahooa
  • 131,293
  • 12
  • 98
  • 101
0

I know it's a an old question but it's possible using f strings.

my_variable = some_object
f'{my_variable=}'.split('=', 1)[0]

this would print 'my_variable'

gioxc88
  • 349
  • 1
  • 9
0

The f-string can do this:

def log(obj):
    print(f"{obj=})

x = 5

log(x)

>>> x=5
mapadj
  • 181
  • 2
  • 3