
Python's sad, unimaginative Enum - andrewcooke
http://www.acooke.org/cute/Pythonssad0.html
======
raymondh
I think the author is sad for the wrong reason.

Syntactic nitpicking is bikeshedding. Remember, Guido makes choices that may
not be obvious unless you're Dutch, but he has a pretty good track record
overall.

A better critique would focus on whether the language actually needed an Enum
construct. Most people get by most of the time without it. Python already
provides many ways to do it (module variables, class variables, etc).

The main problem with Enum is that you won't be able to ignore it. Enums will
start popping-up in many modules and packages, so it will have to become part
of your core Python knowledge (things you have to teach to beginners so they
can work with existing code).

Contrast that with named tuples. They can be ignored (i.e. you can treat them
like regular tuples and you'll get by just fine). Also, named tuples are
profoundly more useful (i.e. using them is one of the easiest ways to improve
the clarity of your code).

~~~
eliben
And yet most large Python projects reinvent the concept of enums in one way or
another. This includes Python itself, where enums could be used in many places
in the stdlib. Actually, one of the main reasons IntEnum is part of the
implementation is that it will be possible to replace stdlib constants with it
(for example some socket.* and io.* constants).

This was discussed in the mailing list in the past, and I even had a prototype
of CPython with constants replaced by IntEnum. Due to its being _an actual
int_ it retains backwards compatibility while providing very nice printable
representation for the constants.

~~~
raymondh
Clearly, you're a big proponent of Enums needing to be in Python (I would
estimate that you've made several hundred emails, comments, and posts on the
subject).

You and a couple other vociferous proponents completely drown-out the early
commenters who valued language compactness and learnability over a pervasive
new construct that solves a somewhat unimportant problem.

The fact that some large projects may have a use for Enums wasn't balanced
against an impending avalanche of Enums being sprinkled in beginner code,
small projects, recipes, etc. I expect the use of Enums will become pervasive
simply because they're there, not because a given snippet of code actually
needs it.

As a person who teaches Python to engineers, I dread adding yet another have-
to-know construct to the cirriculum. Python is no longer a small language.

Also, there is a lot of machinery behind Enums (killing a mosquito with a
cannon). So, we should expect that people will hack it, abuse it, twist it
into knots, subclass it, invent tricks will it, develop funky idioms, etc.
IMO, none of this is worth it. The "problem" wasn't that important to begin
with.

That said, the discussion is moot. There is no need to defend the proposal any
more. Guido has accepted the PEP and it will enter the language, for better or
for worse.

If we had to have a Enum, the one that was accepted was a reasonable choice.
For the most part, the discussion did a nice job of considering existing
recipes, possible use cases, and being as Pythonic as possible.

My only criticism of the process is that handful of enthusiasts (Eli, Ethan,
Barry, etc) launched an avalanche of implementation and api-choice emails that
buried and ignored the posts suggesting the language would be better-off
without Enum. AFAICT, there was no honest discussion of simply establishing
best practices using existing tools.

~~~
eliben
That's not an entirely fair way to put it, Raymond. Yes, some folks said that
maybe Python doesn't need enums at all. But many more said Enums _should_ get
into Python, including Guido. The issue was sealed in a face-to-face
discussion during the recent PyCon language summit in which many core devs
participated. So presenting this as some evil ploy to "drown the opposition"
in a flood of email is... unfair.

Naturally I participated in the discussion, but the vast majority of emails I
sent was to steer it long after the decision to add some kind of enumeration
was made.

------
pekk
Cooke's suggestion is pure special-case magic:

    
    
      class Colour(Enum):
          red
          green
    

If Enum is a class, making a subclass should make a subclass like normal. Not
change how code is parsed within the body of the class definition!

And the same applies to subsequent imaginary suggestions.

If you want a syntax change then introduce a new 'enum' keyword to signal that
we are dealing with a new syntax.

Similarly, if you want the statement 'red = 1' to result in Color.red ==
'red', that is magic. It doesn't make sense. It should give you 1. The value
you just assigned it.

I agree that passing a string to make a class (in the way of namedtuple) is
ugly and undesirable and I would never use it.

But I don't think Mr. Cooke has made any suggestion that is better than
"design by committee". Code which looks like Python, in a Python file, should
behave like Python rather than throwing bizarre curveballs like implicitly
turning 1 into "red" or a bitfield.

~~~
JeremyBanks
I don't agree with his suggestion, but it wouldn't require a change to the
parser. You can already pull that off using metaclasses. Something like this
(untested):

    
    
        class EnumBodyDict(dict):
            def __init__(self, *a, **kw):
                self._keys_accessed = []
                dict.__init__(self, *a, **kw)
    
            def __getitem__(self, key):
                self._keys_accessed.push(key)
                return dict.__getitem__(self, *a, **kw)
    
        class EnumMeta(type):
            @classmethod
            def __prepare__(metacls, name, bases):
                return EnumBodyDict()
    
            def __new__(cls, name, bases, classdict):
                next_enum_value = max(classdict.values()) + 1
    
                for name in classdict._keys_accessed:
                    if name not in classdict:
                        classdict[name] = next_enum_value
                        next_enum_value += 1
    
                return type.__new__(cls, name, bases, classdict)
    
        class Enum(object, metaclass=EnumMeta):
            # proposed enum implementation here

~~~
eliben
You're right, this can be done without changing the Python syntax and in fact
there was a proposal (with implementation) on the table for a long time. But
eventually it was deemed to be too error prone and the more explicit approach
was chosen. Python often resolved a tradeoff in favor of explicitness, and
IMHO this ended up being a good decision.

------
eliben
Every point raised by this post was discussed and rehashed multiple times,
debated, argued over, complete with emotions. The end result was something
that feels right to the vast majority of the discussion participants (give or
take a few minor details.) Anyone has the right to criticize, and anyone also
has the right to participate in the design and decisions - this is open
source. Sadly way more people choose to take advantage of the former freedom
than of the the latter. But that's old news, just the way things are.

Every language/framework/system has features some people won't like. Python
has many too. But Python did a great job at striking a careful balance between
expressibility and syntax, and most programmers who have to deal with it love
it. Enum's design was guided by the same principles. Given that we didn't want
to add new syntax to the language just for this feature (Python's minimal
syntax is one of its greatest strengths), we had a few challenges to face. We
tried to follow Python's overall guide of explicitness, while allowing
numerous features.

The end goal? To replace the plethora of hand-cooked enums almost every large
Python project has (including examples within Python itself).

Finally, remember that many issues here are issues of style and personal
preference. It's not far from a brace style war really.

------
raymondh
One minor correction about named tuples. The syntax for field names can be an
iterable or a string. The preferred way to do it is:

    
    
        > Person = namedtuple('Person', ['name', 'age', 'gender'])
    

The string form is there to accomodate use cases where the field names are
being cut-and-pasted from SQL or CSV headers or somesuch. Also, some people
find it easier to type:

    
    
        > Person = namedtuple('Person', 'name, age, gender')

~~~
eliben
All the same ways were chosen for the Enum functional API, by the way (list,
dict, string separated with either spaces or commas). In general, the
functional API was inspired mostly by namedtuple. It's not that the namedtuple
syntax is pretty. It's not, we all know that, but that's what Python can offer
at this point. But it's good enough so people learn to live with it, get used
to it and intuitively understand what it means, and that's good enough.

That said, the preferred and recommended approach to define enums is with the
class syntax.

------
NelsonMinar
"only real achievement is not doing anything new". That sounds like an
excellent endorsement for a new Python language feature. Leave the crazy
syntax innovation for hipster languages with unreadable, poorly thought out
features. I've got work to do in Python.

------
batterseapower
All enum types look a bit sad when you're used to the full algebraic sum types
+ pattern matching provided by a functional language

~~~
tomp
But sometimes you only need enums (which are, arguably, a subcategory of
ADTs). E.g. playing card suits, true/false, ...

~~~
gnaritas
True and false work fine as singleton subclasses of the abstract class
Boolean; see Smalltalk.

~~~
tomp
What prevents me from making a third subclass, Undetermined?

ADTs and enums are meant to prevent this.

~~~
gnaritas
> What prevents me from making a third subclass, Undetermined?

Smalltalk isn't about preventing things, it's about enabling them.

------
btipling
A shallow nitpicking of language syntax. I would rather read about how a
programming language fares at solving difficult problems. Not a minor quibble
that has no impact on anything other than this programmer's sensibilities.
It's the kind of thing that makes me think people like this are a type, a type
that want to abandon semicolons and jump on languages that look better but
perform worse.

~~~
ansgri
Semicolons are a triple cognitive overhead (errors from omitting them,
irritation that they add no value in well-formatted code, and ability to write
very poorly formatted code), where their absence has the only overhead of
unnatural ways of breaking long lines, so typical programmer performance
should be better.

------
aiiane
I actually disagree with basically all of the points raised in this article.
Sure, apparently Enum doesn't fit your particular desires for it - but there's
good reasons why those choices were made.

    
    
        Colour = Enum('Colour', 'red, green')
    

There's a difference here between 'name' as in where in the namespace a value
is listed, and 'name' as in what descriptive name something has. For instance,
if you were to do this...

    
    
        Colour = Enum('Colour', 'red, green')
        Tint = Colour
    

Would you want the values from Tint to suddenly say that they're different
values from the values in Colour, even though it's the same underlying object?
I doubt it.

Similarly, regarding having to assign numbers: this makes a hell of a lot of
sense any time you're trying to serialize things for interoperation between
multiple services. There's a reason why protobufs do it this way as well: you
get really powerful backwards-compatibility options, which you might not
realize you need at first but become extremely handy down the line.

~~~
TheSilentMan
I think the argument about specifying values is that it should be optional,
not that it shouldn't exist.

~~~
kemayo
Well, it is optional, in the sense that there's a provided syntax which
doesn't require it. (The namedtuple-esque one.)

------
samps
OP: if you want bitmask-style enums, that's easy to code up with a derived
class. The reference implementation shows how:
[https://bitbucket.org/stoneleaf/aenum/src/0c66e1a5ec7b76388a...](https://bitbucket.org/stoneleaf/aenum/src/0c66e1a5ec7b76388a3bab5403fdcf4729b9802c/aenum.py?at=default#cl-420)

~~~
d0mine
Below is the excerpt from the module's docstring:

    
    
      On Wed, Feb 27, 2013 at 2:03 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
      > I'm beginning to see why enums as a class has not yet been added to Python.
      > We don't want to complicate the language with too many choices, yet there is
      > no One Obvious Enum to fit the wide variety of use-cases:
      >
      >   - named int enums  (http status codes)
      >   - named str enums  (tkinter options)
      >   - named bitmask enums  (file-type options)
      >   - named valueless enums  (any random set of names)
      >   - named valueless-yet-orderable enums  (any not-so-random set of names 
    
      That's probably the best succinct description of the core problem I've
      seen, and I've been following the various enum-related dicussions for
      years
    
      Cheers,
      Nick.
    
      -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

------
elehack
Mildly unrelated, but when I think of the opposite ("imaginative" enums), the
first thing that comes to mind is this amusing vignette on the design process
of Java's enums: <http://blog.des.no/2009/01/no-silly-its-enum/>

~~~
brazzy
How embarrassingly narrow-minded. Yeah, Java's enums are fully fledged
classes. Which actually makes them extremely powerful and useful, more useful
than enums in any other language. I've seen more than one C programmer unable
to cope with such an extreme deviation from their bunch-of-integer-constants
expectation.

~~~
elehack
Yeah, Java's enums are very powerful and useful.

But in the context of a C-style language, I can understand the "what? these
are enums. why do they have methods? and _subclassing_?"

It might be good if they had a different name.

~~~
brazzy
The essential property (a type having a fixed number of named instances) is
still there, so the name is fine.

I can fully understand that someone used to C enums would go WTF at seeing
Java enums for the first time. What makes it narrow-minded is when this leads
you to rejecting them outright and writing blog posts to ridicule them.

------
lclarkmichalek

      > What would you expect a Pythonic enum to look like?
      >
      > class Colour(Enum):
      >     red
      >     green
    

Hell no. I mean, even Go isn't that implicit, you still have to use the iota
there. Anyway, this was considered, and you can see the reasons against it
(standard explicit is better than implicit) here:
<http://www.python.org/dev/peps/pep-0435/#id38>

    
    
      >  class Colour(Enum):
      >      red = 1
      >      green = 1
      >
      > Still, at least the mistake above would raise an error.  Wouldn't it?  Nope.
      > That's a feature.  If you mess up on the non-optional values then you get
      > "aliases".
    

Aliases are a feature, like it or not. Having 100% unique enums would not
actually be a good thing, seeing as how sometimes libraries support multiple
names for the same enum value for backwards compatibility reasons.

And if you do actually want to prevent the duplication problem, you can do
this

    
    
        class Color(Enum):
            red, green, blue = range(3)
            
            red_alias = red
    

Or alternatively, if having to specify how many values is too unimaginative
for you, something like

    
    
        class Color(Enum):
            red, green, blue, *_ = range(2**128)
            
            red_alias = red
    

will also work.

    
    
      > So, you go hunting around in the docs to see if there's any way at all of
      > avoiding the need to assign values manually.  And there is:
      > 
      >   Colour = Enum('Colour', 'red, green')
      >
      > which suffers from the same problems as namedtuples:
      > - you need to repeat the class name (in a string, which your IDE is
      >   unlikely to check)
      > - the parameters are themselves in a string, which your IDE is 
      >   unlikely to parse and provide in auto-complete (they can be separate
      >   strings, in a sequence, but that doesn't really help).
      >
      > Now if two potentially useful library classes are suffering from the same
      > problems than isn't that a BIT OF A HINT to try make things better?  Nope.  It
      > just shows how important it is to not be imaginative.  Or something (crack).
    

It's true! IDEs don't deal well with meta-programming, and Python isn't Lisp!
I don't love the namedtuple syntax particularly either, but what solution
would you propose? There doesn't seem to be an easy way around it, afaict.

    
    
      > And it gets worse.  What values do you think the above provides?
      >
      > Strings?  That would makes sense (red = 'red'), in that it would display
      > nicely and is going to provide easy to debug values.  So nope.
    

Well, if we read the PEP (<http://www.python.org/dev/peps/pep-0435/#id26>):

    
    
        >>> 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
    

We see that printing an enum value gives the string representation, so I don't
really think that it not defaulting to strings is that much of a problem.

    
    
      > Integers from zero?  I mean that's how Python counts indices and there's "only
      > one way to do it" so that's how Python counts enums, right?  Nope.
    

I think I almost agree with you here. That said, zero does have a specific
meaning in a boolean context, so I can see why they didn't go for it.

    
    
      > OK, so bit fields?  That way we can do cool Colour.red | Colour.green and
      > make the C programmers feel at home?  Nope.
    

Aside from the whole never going to happen argument, I think this kind of
shows the problem with doing this implicitly; whatever you do, it's not going
to work for some people.

    
    
      > Give up?  I'll tell you.  It counts from 1.  Presumably because it's really
      > common to use the "if not enum" idiom.  In someone's crack-addled dreams.
    

Hey, don't despair so much. You can always read the PEP and realise you can
actually provide a dictionary as the second argument. So maybe you'd prefer

    
    
        def MyEnum(name, vals):
            return Enum(name, {val: i for i, val in enumerate(vals)})
    

to have your enums start from 0. I should mention, this information is in the
PEP: <http://www.python.org/dev/peps/pep-0435/#id35>

~~~
omaranto

        class Color(Enum):
            red, green, blue, *_ = range(2**128)
            
            red_alias = red
    

Does that create a list of size 2^128-3 =
340282366920938463463374607431768211453 and assign it to Color._?

~~~
lclarkmichalek
Ha! Too much Go for me. It does actually, my bad, I expected the variable to
be ignored (and the range creation isn't a big deal usually as it's just a
generator).

~~~
wasabian
Using range would create the whole list. xrange is the one that acts like a
generator :)

~~~
andreasvc
This Enum is slated to be introduced in Python 3.4, where range is (not acts
like) a generator.

~~~
wasabian
Ah. I stand corrected :)

------
laureny
As much as I have qualms about Java, if there is one thing that it perfectly
nailed, it's enums.

The Java enums can be as concise as the C/C++ one but since it's also a class,
you can add all kinds of interesting stuff to decorate it such as
constructors, getters, type converters, etc... Really a pleasure to work with.

~~~
bhickey
I have a single gripe with how Java handles enums.

They would be safer to work with if it was invalid to write a switch statement
with a missing case and no default. I see a lot of code like:

switch (foo) { case Bar: ... case Baz: ... default: throw new
IllegalArgumentException(); }

~~~
Too
I don't know the state of lint-tools for java but for C and C# most lint-tools
do this check.

------
jrockway
I personally _use_ enums more than I declare them, so I don't mind a little
bit of namedtuple-like repetitive syntax. I'm already used to namedtuple, so I
don't see anything wrong with reusing that syntax for enum. It's Python, not a
brand-new programming language.

I will admit I kind of like Java's syntax for enums because it makes it very
clear what exactly an enum is: a class with a bounded set of instances:

    
    
      enum Month {
        JANUARY(31, "Jan"),
        FEBRUARY(28, "Feb"),
        MARCH(31, "Mar"), 
        ...;
    
        Month(int days, String abbreviation) { ... }
      }
    

(But honestly, Java enums have a number of annoyances and feel very "tacked
on" like generics do.)

------
overgard
I never got python's lack of built in enum syntax. I get not wanting to add
unnecessary keywords, but unless you're doing something like Scheme or
Smalltalk where you're going for almost no syntax at all, you would think it
would pay off to put in keywords for things that literally occur in every
program. I feel like I should just be able to write this:

    
    
       enum Color: red, green, blue 
    

Right now enumerations are a mess. It seems like every project does them
slightly differently, and the new enum library just adds a new way to do them
slightly differently. That's like the opposite of PEP 8.

~~~
kemayo
Well, any new enum syntax would be "a new way to do them slightly
differently". This one just has the advantage of being in the standard
library, and thus is presumably what new usage will gravitate towards.

------
dlitz
Presumably, you should also be able to do something like this:

    
    
        class Colour(Enum):
            red, green, blue = range(3)

~~~
sn6uv
range(1,4)* because it counts from 1 (range counts from 0).

~~~
Tobu
Starting from zero solves the "one right way to do it" problem which is
mentioned in the blog post.

~~~
marcosdumay
Yes, but recreates the "a value must be true in a boolean context" problem
discussed in the PIP.

------
QuixoticChris

        Colour = Enum('Colour', 'red, green')
    

That is beyond sad.

~~~
Myrmornis
Yep, never understood it. I don't see the use case of pasting from csv or
whatever.

------
Vivtek
Best first two comments ever, though.

------
Tmmrn
Someone on stackoverflow posted a nice hacky way:

<http://stackoverflow.com/a/1695250>

Even with reverse mapping.

------
nayefc
Now I appreciate Ruby symbols 10x more.

------
Demiurge
Yes, it's an emotional Enum according to emotional programmers who have strong
feelings about the matter!

------
badgar
What's up with Peter Norvig's comment at the bottom? Did he not RTFA? He
suggests a solution that the article criticizes heavily halfway through.

~~~
kami8845
Why is it OK to be rude through abbreviations?

You'd get heavily downvoted for asking "Did Peter Norvig not read the fucking
article?", yet that's exactly what you've put into everyone's mind with your
comment.

~~~
philwelch
Initialisms like "RTFA" lose their literal intensity through overuse.

------
smegel
A stdlib implementation is a whole lot easier and less disruptive than new
syntax in core. This counts for a lot. But of course you're vastly more
limited in what you can do. The main benefit of this proposal that i can see
is not the specific functionality it provides, but simply the fact that it
makes it clear and explicit that this is a set of constants that have some
meaning to the module - putting such constants in ordinary classes/namespaces
(along with everything else) can obscure the fact they are not
variables/attributes.

------
nilved
I've still yet to find a good use case for Enum. It's (lousy, inconsistent)
syntactic sugar that none of my code will use.

