[Python-ideas] PEP XXX - Competitor with PEP 435: Adding an enum type to the Python standard library

Andrew Barnert abarnert at yahoo.com
Wed Mar 13 02:43:44 CET 2013

> From: Alex Stewart <foogod at gmail.com>
>Sent: Tuesday, March 12, 2013 3:17 PM

>Regarding the issue of bitmask-enums, I do agree that they are common enough in various APIs that it is important that we be able to support them easily.  However, I have yet to see how or why they are actually different than int-enums in any practical way.  I don't see why we need to treat them as a different category at all and I see no value in doing so.  They're all just int-enums.  Problem solved.

I agree with this.

If there's an easy way to specify enum values, there's no reason that specifying 1, 2, 4, 8 has to be any different from specifying 0, 1, 2, 3.

And, if there's a general-purpose way of "compounding enums" that lets me get the OR-ed value for passing to C/stdlib/etc. APIs that want that, I have no need for a special-purpose way of OR-ing bitmask enums.

So, given all that, there is no value in treating them as a separate category. And it looks like maybe you've got a good answer that gives all that below, so…

>For clarification:  I believe that what we're fundamentally talking about here is the question of "compounding" enum values into what are effectively multi-enum sets (they are not really the same thing anymore as a single enum value, because they do not have a one-to-one correspondence with any one enum value).  With ints, this compounding operation is typically a "binary or" operation.  This is what we call "bitmasks", but really they're just a particular way of compounding ints together.

I suggested, a while back, that we could have some kind of EnumSet along with Enum, which sounds like what you're talking about here. So, if I did this:

background = Color.RED | Color.BLUE

What I get is not an Enum of type Color with a value RED | BLUE, but an EnumSet or type Color with a value {RED, BLUE}. Your CompoundEnum seems to be the same thing, except that it puts the "enum-ness" first instead of the "set-ness", which is probably an improvement.

There are a few tricky bits here—most of which I didn't think through when I first suggested this, but it looks like you may have.

You need a 0 value. After all, "background & Color.GREEN" has to return _something_. What is it called, what are its str and repr, etc.?

You also need negated values, or there's no way to write "background &= ~Color.RED". So, "~Color.RED" has to be something. Presumably a CompoundEnum, with an int value of -2 (since RED is 1). But what are its str and repr? Does this mean internally CompoundEnum is a set of values and a set of negated values? If you want to make CompoundEnum work for string enums, what's the negation of a string value?

Getting the list of interactions between Enum and CompoundEnum exactly right seems non-trivial. For example, "Color.RED in background" has to work even if background is just plain "Color.RED". (Otherwise, you'd have to write horrible stuff like "background == Color.RED or Color.RED in background".) You can't just say "both types support all numeric and set operations in the obvious way", because I don't think it's obvious that, e.g., __or__ treats either type as a set while __lt__ treats either type as a number. In fact, in some cases, I don't even know what the answer would be. Can __contains__ take a CompoundEnum on the left side? What's the __len__ of a CompoundEnum with negated values in it? And so on…

Finally, what happens if someone has an int Enum whose values aren't unique bits, and tries to create a CompoundEnum out of it? If RED=1, BLUE=2, GREEN=3, is RED|BLUE an error? If so, that means that common cases like RDWR=3, ALL_FLAGS=0xFFFFFFFF, LOCAL_FLAGS=0x8FFC, etc. are errors too. On the other hand, if that's allowed, does that mean READ|WRITE gives me RDWR, or a CompoundEnum that compares equal to RDWR, …? For that matter, what about RDWR | ~READ? (And can I define ANTIRED=-2 so ~RED = ANTIRED?)

If you've solved these problems, then yes, CompoundEnum completely eliminates the need for BitMask. And it sounds like, even if you haven't, you're at least well on your way to doing so. I'm looking forward to seeing it.

More information about the Python-ideas mailing list