[Python-ideas] Enums

Guido van Rossum guido at python.org
Thu Jul 28 04:21:46 CEST 2011


On Wed, Jul 27, 2011 at 6:12 PM, Nick Coghlan <ncoghlan at gmail.com> wrote:
> On Thu, Jul 28, 2011 at 10:56 AM, Barry Warsaw <barry at python.org> wrote:
>> Again, looking at how I've used them extensively over the last several years,
>> I would much rather write
>>
>>    class Colors(Enum):
>>        red = 1
>>        green = 2
>>        blue = 3
>>
>> than
>>
>>    red = NamedValue('red', 1)
>>    green = NamedValue('green', 2)
>>    blue = NamedValue('blue', 3)
>>
>> To me, the duplication is jarring and error prone.

Agreed, especially in new code, when you just want to get the names
defined, and you don't have to worry about compatibility. I think
that's why NamedValue would often be wrapped by an enum class. But
there is the downside that, for all the elegance of writing

class Color(enum):
  red = 1
  ...

all the *uses* of colors have to write foo.Color.red instead of foo.red.

> Yeah, I'd actually be inclined to define such values programmatically
> rather than writing them out manually like that:
>
> _named_colours = dict(
>  red=0xFF0000,
>  green=0x00FF00,
>  blue=0x0000FF,
> )
> globals().update((k, namedvalue(k, v)) for k, v in _named_colours)

Eek, no! It will take the average reader way too long to figure out
that that does. Static analyzers (which are getting more important)
are likely to be fooled by it too. Please remember EIBTI.

> (where namedvalue is the value based factory function I mentioned in
> the recipe post)
>
> However, my contention is that the fundamentally interesting operation
> is associating names with values (as your EnumValue class does). Enums
> and their ilk are then just syntactic sugar for defining groups of
> such values without needing to repeat yourself.

It's just possible that there's no way to define enums that neither
introduced a new (class) scope nor requires a lot of redundant typing.

I wish I could write

  red = Enum(1)

and it made the following true:

  assert red == 1
  assert isinstance(red, int)  # a subclass
  assert str(red) == 'red'

But we'd first need a non-hacky way for Enum() to know that it is
being assigned to something named 'red'. I have a few other use cases
for that as well, e.g. a Property class for App Engine that doesn't
require a metaclass to patch up the value.

A half-solution would be if naturally the definition of red appeared
inside some other class (not a class specially created for the enum,
but a class that has other functionality) and somehow a mix-in
metaclass (if such a beast exists) would pass in the name once the
class is being defined. But if you want module-level enum values you'd
still have to do something like

  class Color(Enum):
    red = 1
    ...

  red, green, blue = Color.red, Color.green, Color.blue

That's not ideal since the next programmer could add a new color but
forget to also pull it into the outer scope. And no, I don't like
solutions based on globals()...

TBH, I'm not sure we should hold our breath until we have the perfect
solution. Looking over flufl.enum again, I like most of its design
decisions except the "enums are not integers" part. For me, after the
above definition of class Color, I'd be happy of Color.red == 1, as
long as str(Color.red) == 'red'. (Here I am consistent with the
behavior of True and False.) If you want the enum values to appear at
the module level (e.g. socket constants?) pulling them up a level
explicitly, while not ideal (see above), is not the end of the world
either.

-- 
--Guido van Rossum (python.org/~guido)



More information about the Python-ideas mailing list