top | item 5685903

PEP 435 Accepted – Adding an Enum type to the Python standard library

188 points| randlet | 13 years ago |python.org | reply

106 comments

order
[+] e1ven|13 years ago|reply
Can someone help me understand how this helps? I'm only a mediocre programmer, and I'm having trouble understanding the implications.

It seems like you declare these like any other class. So what's the difference between this, and creating a class full of ints?

If I have class Color: red = 1 blue = 2

etc, wouldn't I get the same thing?

Is the difference that you don't need to instantiate an instance of it?

I know I'm being dense, and I've read the PEP, but it's not quite clicking yet.

[+] famousactress|13 years ago|reply
In this case, the attributes are not ints.. so If you defined that class functions that took it's attributes would need to accept an int, and if I passed max-int or something it'd be up to your function to go figure out whether my argument was a valid value for Color.

The enum class gives you that stuff. The argument you'd expect would be instanceof(arg, Color) == True (I think that's how they did it) and if it was, you'd be assured that the value was a valid value for Color.

So you could do it yourself, sure.. but to get all the little benefits, you'd have to do more coding than naming a bunch of ints in a new class.. It's subtle though. Not a wild game-changer or anything :)

[+] dietrichepp|13 years ago|reply
In addition to what famousactress said, you also get:

    >>> Color.red
    <Color.red: 1>
So when you print out an enumeration, you get its name instead of just the number. This can be a big deal for debugging e.g. OpenGL code, which has a lot of enumerations. Which of the following would you prefer?

    >>> gl.getError()
    1286
Or would you prefer,

    >>> gl.getError()
    <gl.INVALID_FRAMEBUFFER_OPERATION: 1286>
[+] pyre|13 years ago|reply
Read the section on IntEnums[1]:

  IntEnum values behave like integers in other ways
  you'd expect:

  >>> int(Shape.circle)
  1
  >>> ['a', 'b', 'c'][Shape.circle]
  'b'
  >>> [i for i in range(Shape.square)]
  [0, 1]

[1] http://www.python.org/dev/peps/pep-0435/#intenum
[+] comex|13 years ago|reply
The syntax strikes me as somewhat strange. It requires writing out enum values (and thus avoiding duplicates or, for a cleaner look, just keeping them sorted), necessitating unnecessary effort when new values are added in the middle, and misleadingly implies that the enum values are merely class-level integer constants. I would have preferred a more explicit API that used a list of strings for the enumerators.
[+] famousactress|13 years ago|reply
As an optional implementation that'd be nice, but this syntax I assume is meant to solve two problems:

1. Making the integral values of an enum clear and unchanged by re-ordering the names. If the integral values matter to you (because you serialize enums that way), then I would want them to be explicit.

2. The integer values of these enums aren't exclusive. Two enums can reference the same value.

That said, I agree a short hand for people who don't care seems reasonable, where the integers could be auto-assigned by definition order, or just all set to 0.

[+] oellegaard|13 years ago|reply
I completely agree, this disgusts me:

>>> Animal = Enum('Animal', 'ant bee cat dog') >>> Animal.ant <Animal.ant: 1>

That being said, I welcome enums - I made classes to basically represent an enum a bunch of times.

[+] Tobu|13 years ago|reply
The syntax is easy for us who have been using ghetto enums:

  class Colors:
      red, green, blue = range(3)
[+] chrismorgan|13 years ago|reply
Adding new values in the middle is exactly why it needs to be explicit. If you're pickling them, for example, or otherwise recording them, you certainly care about the values. Auto-assignment is often convenient, but it certainly must never be the only way to do it.
[+] encoderer|13 years ago|reply
Would love to see functionality here similar to the iota keyword in Go.
[+] adrianmsmith|13 years ago|reply
I don't think that enums should have values (i.e. numbers).

Nearly every language does it that way but isn't that just because C originally implemented enums with numbers? Modern languages are much higher level.

Java enums don't have any numeric equivalent value, they are just objects, with textual names if you must convert them into something more primative.

[+] eliben|13 years ago|reply
Enum values don't have to be ints, they can have any value you want. The explicit values are there because Python's "explicit is better than implicit" zen, and the functional API gives you a way to create enums without assigning explicit values if you really want to.
[+] famousactress|13 years ago|reply
I always thought the fact that Java enums don't have numeric values ended up being a nice idea, but a mistake.. specifically because .ordinal was still exposed, and ends up getting used by people who want to serialize the enum as something other than a string... .ordinal of course is affected by the order the enum is defined in, which is sketchy.

My memory could be a little faded on this though, I haven't coded in Java in a few years..

[+] Nursie|13 years ago|reply
Efficiency/speed?

The user doesn't necessarily need to have access to the raw numbers I guess, but when it comes down to comparisons/branching etc, numbers are pretty much always going to be fastest and simplest.

And if they are going to be based on numbers underneath, why not let the user/coder/whatever have access to them?

[+] kstrauser|13 years ago|reply
One approach would be to set values like "red = object()". I use that often to create guaranteed-unique sentinel values.
[+] sp332|13 years ago|reply
Doing binary arithmetic on enums can be very useful! First | Second = Both.
[+] mattmanser|13 years ago|reply
It can actually be pretty useful to be able to add them up and then use them in or statements.

Examples from C#:

http://msdn.microsoft.com/en-us/library/vstudio/cc138362.asp...

It's also used to be sort of useful for serialization or storing them in a DB as they'd take up less space than a string, but these days that's not so important and I'd actually caution against it.

Basically you don't need it often, but when you do it's extremely useful.

[+] marmaduke|13 years ago|reply

  Enumerations support iteration, in definition order:
  
  >>> class Shake(Enum):
  ...   vanilla = 7
  ...   chocolate = 4
  ...   cookies = 9
  ...   mint = 3
  ...
  >>> for shake in Shake:
  ...   print(shake)
  ...
  Shake.vanilla
  Shake.chocolate
  Shake.cookies
  Shake.mint
Python's execution model sez that the class declaration is nothing more than code that is exec'd in a dictionary. We know that Python dictionaries do not perserve order, so why is this iteration in definition order possible, barring modification to the interpreter or dict type ?
[+] eliben|13 years ago|reply
It's possible by using a __prepare__ method in the metaclass of Enum, which allows to return an essentially ordered dict for the __dict__ of enumeration classes.
[+] jjs|13 years ago|reply
Python has been getting bloated in recent years. Why not just do the following?

  def enum(*args):
      return dict(zip(args, range(len(args))))

  colors = enum('red', 'green', 'blue')

  # {'blue': 2, 'green': 1, 'red': 0}
[+] pavpanchekha|13 years ago|reply
There are a few things this proposal does that improves upon yours:

+ Enum values of different types are incomparable. This is widely seen as a good thing. + Enum values are instances of their Enum; this allows more flexibility in user code. + As a result of the above, you can have a dictionary without Enum values of two different types colliding. + Enum values print in a more friendly way. This is expected to help debugging. + To support the above, enum values know their own name. This is likely helpful both for debugging and for various introspection hacks. + Enums can be iterated over in a fixed order. This allows automated help systems and similar to maintain consistency between runs, improving user experience. + There's a lot more error checking provided to avoid cases like defining the same enum value twice.

But I understand your sentiment. We always enjoy smaller languages because it helps us keep them in our heads. But note that Enums aren't a new language feature -- this PEP simply adds another module to the standard library. The code your provide, flawed as it is, is still a pattern common to many different libraries, so it would be good to put it into the stdlib; but if we're doing that, might as well do it right, don't you think?

[+] jayflux|13 years ago|reply
"Python has been getting bloated in recent years"

Didn't Python 3 do more removing/tidying up than actually adding of stuff?

[+] nilsbunger|13 years ago|reply
When will this ship in python? I don't know a lot about the typical time between a PEP acceptance and a release containing the feature.
[+] eliben|13 years ago|reply
We expect the implementation will be committed in the next few weeks, and you'll be able to give it a try using Python's tip revision ("trunk"). It will actually _ship_ with Python 3.4
[+] morpher|13 years ago|reply
Interesting that this is PEP 435 and the prior attempt was PEP 354.
[+] nobodysfool|13 years ago|reply
Stupid me, I was using enums the wrong way. I was using an enum like a constant.

I.E. If my database takes a 'Sex' parameter, with Male as 1, Female as 2, Unknown as 3, Both as 4 - I'd use an enum like so:

    update person set sex=Sex.Male
Looks like I can't do that with this enum class. Well, I suppose I'd have to do it like so:

    sex = Sex.Male.value
not very sexy...
[+] bsimpson|13 years ago|reply
Wouldn't IntEnum fit your case better?
[+] herge|13 years ago|reply
Could you not use mock.sentinel values as an enum? Also, you can create a poor man's enum from 2.7+ with:

    from collections import namedtuple
    myenum = namedtuple('myenum', ['a', 'b', 'c'])(range(3))

    myenum.a == myenum.a
[+] yen223|13 years ago|reply
What purpose would Enums serve in a dynamically-typed language?
[+] eliben|13 years ago|reply
Read the PEP motivation :-)

It's still useful to have mnemonic names for special values, with nice string representations and some guarantees on what is equal to what.

[+] oellegaard|13 years ago|reply
Even though dynamically typed, it still have the concept of types. There are good examples on how it works in the PEP.
[+] lmm|13 years ago|reply
Seems a bit dodgy that enum values can be any value. What happens if you use mutable values and later change them to be the same?
[+] pyre|13 years ago|reply
You could say that about a lot of things in Python. If you add a constant value as a class attribute, anyone using that class has the ability to change it, even if that would screw things up.
[+] ajanuary|13 years ago|reply
> Iterating over the members of an enum does not provide the aliases

Anyone know the motivation for this? Seems like a source of frustration to me.

[+] dschep|13 years ago|reply
Barry gave a good talk about this at DCPython on Tuesday.
[+] Toshio|13 years ago|reply
Interesting. It looks like if you want to use enum in 2.7 you have to use a package called flufl[1].

[1] http://bazaar.launchpad.net/~barry/flufl.enum/trunk/view/hea...

[+] eliben|13 years ago|reply
flufl.enum was the initial candidate for PEP 435, but the current approach changed and uses implementation techniques not available in Python 2.7

We may create a almost-compliant back-port for 2.7 as an external library though