0

In python 2.7, what's the best / easiest / most "pythonic" way to convert an integer value to the name of a module-scoped constant with that value?

Bonus points if there's a way to scope the search for variable names to those beginning with a magic prefix.

In other words:

EVT_FOO =0x0001
EVT_BAR =0x0002
EVT_SPAM=0x0004
EVT_EGGS=0x0008

def getEvtName( val ):
    """ This should return 'EVT_FOO' when given the number 0x1 """
    pass 

I know I could just create a dictionary to do a reverse-lookup, but I'd like to think there's a better way in Python. Suggestions?

Brian McFarland
  • 9,052
  • 6
  • 38
  • 56
  • 2
    What if `val` is assigned to more than one name (not unlikely, if it's a small integer)? Why not use an `Enum` (either the backport or one of the other suggestions - see http://stackoverflow.com/q/36932/3001761)? What is the *"better"* by which you plan to measure alternatives to a dictionary (and/or what's the problem with a dictionary)? – jonrsharpe Jul 14 '15 at 16:29
  • This is for a small `ctypes` wrapper that covers 2 functions, on that accepts a bitwise-or-ed together mask of events to watch, and one that returns a single event at a time. Its simple enough and small enough that I'm sure I'll never have a collision unless an end user adds a value to the module post-import. – Brian McFarland Jul 14 '15 at 18:12
  • By "better" I mean less code I have to write and maintain. Performance is of secondary concern since I want this mostly for debugging/testing. A reverse lookup dictionary would add 30 lines to a module that's only 75 lines to begin with. I'd like to have the constants stay at the module level so I can do modulename.SOME_CONSTANT in the code that uses it.... unless I'm missing something, I think that rules out Enum equivalents for 2.7, right? – Brian McFarland Jul 14 '15 at 18:17

1 Answers1

1

You can use globals() function to get the name if a global object :

>>> EVT_FOO =0x0001
>>> EVT_BAR =0x0002
>>> EVT_SPAM=0x0004
>>> EVT_EGGS=0x0008
>>> 
>>> def getEvtName( val ):
...     return next((i for i,j in globals().iteritems() if j==val and i.startswith(something)),None)
... 
>>> getEvtName(0x0001)
'EVT_FOO'
Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • Works like a charm with one exception, it throws `StopIteration` if the value is not found. Depending on application, other users may want to return `None` or an empty string instead. Also I ended up adding a simple `i.startswith()` condition to narrow the search-space from all global symbols. – Brian McFarland Jul 14 '15 at 18:41
  • 1
    @BrianMcFarland The `next` function accepts a default value at its second argument. check the edit! – Mazdak Jul 14 '15 at 18:48