3
\$\begingroup\$

There have been many posts here about enums in python, but none seem to be both type safe and simple. Here is my attempt at it. Please let me know if you see anything obviously wrong with it.

def typeSafeEnum(enumName, *sequential):
    topLevel = type(enumName, (object,), dict(zip(sequential, [None] * len(sequential))))
    vals = map(lambda x: type(x,(topLevel,),{}), sequential)
    for k, v in zip(sequential, vals):
        setattr(topLevel, k, v())
    return topLevel

Then to test it:

Colors = typeSafeEnum('Colors', 'RED', 'GREEN', 'BLUE')

if __name__ == '__main__':
    x = Colors.RED
    assert isinstance(x, Colors)
\$\endgroup\$
3
  • 1
    \$\begingroup\$ What do you mean by "type safe"? \$\endgroup\$
    – Armin Rigo
    Commented Mar 30, 2013 at 11:46
  • \$\begingroup\$ Good point. It should not be comparable to other types. E.g., you should not be able to accidentally use an int instead of Colors.RED \$\endgroup\$
    – user1094206
    Commented Mar 30, 2013 at 11:51
  • 1
    \$\begingroup\$ Note for posterity: as of Python 3.4, there is a standard enum module and it's available via pip install enum34 for older versions. \$\endgroup\$
    – o11c
    Commented Jul 16, 2015 at 19:57

3 Answers 3

3
\$\begingroup\$
def typeSafeEnum(enumName, *sequential):

Python convention is lowercase_with_underscores for functions and parameters. Although since this is a type factory its not totally clear what convention should appply. I also wonder if passing the enum values as a list might be better.

    topLevel = type(enumName, (object,), dict(zip(sequential, [None] * len(sequential))))

There is no point in the dict you are passing since you just setattr all the enums in place already. Here you set all the enum values up with None initially and then fill them in. Why? I can also see why you call this topLevel but its not an immeadiately obvious name

    vals = map(lambda x: type(x,(topLevel,),{}), sequential)

List comprehensions are generally preffered to calling map with a lambda. But also, why? Just do this in the for loop. You don't gain anything by having it done in a map and then zipping over it.

    for k, v in zip(sequential, vals):

I suggest longer less abbreviated names

        setattr(topLevel, k, v())
    return topLevel
\$\endgroup\$
2
  • \$\begingroup\$ Thanks, Winston! I will do some revisions and post a new version. Besides the style and code cleanliness, does the approach seem correct to you? \$\endgroup\$ Commented Apr 1, 2013 at 15:14
  • \$\begingroup\$ @user1094206, I avoid enums generally in favor of actually defining classes for what would be an enum value. That way I can take advantage of polymorphism and such. So stylistically, I wouldn't use this approach at all. \$\endgroup\$ Commented Apr 1, 2013 at 15:30
3
\$\begingroup\$

This seems like a problem to me:

>>> type(Colors.RED) is type(Colors.GREEN)
False
\$\endgroup\$
1
\$\begingroup\$

I'm not sure why you would want to use a Java idiom in Python. The problems TypeSafe Enums fix in Java are not really issues in Python as far as I can tell.

If you can use a regular enumeration you can use collections.namedtuple.

from collections import namedtuple
Colors = namedtuple('Colors', ['RED', 'GREEN', 'BLUE'])._make(xrange(3))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.