[Python-ideas] PEP for enum library type?

128 views
Skip to first unread message

Eli Bendersky

unread,
Feb 12, 2013, 10:21:04 AM2/12/13
to python-ideas
Hi all,

The ideas thrown around and Tim's prototype in the thread "constant/enum type in stdlib" show that it's possible to use some metaclass magic to implement very convenient syntax for enums, while only including them in the stdlib (no language changes):

from enum import Enum

class Color(Enum):
  RED, BLUE, GREEN

I'm, for one, convinced that with this in hand we should *not* add special syntax for enums, since the gains would be minimal over the above proposal.

While the parallel thread (and Tim's Bitbucket issue tracker) discusses his proposed implementation, I think it may be worthwhile to start from the other direction by writing a PEP that aims to include this in 3.4. The way I see it, the PEP should discuss and help us settle upon a minimal set of features deemed important in an enum.

If that sounds OK to people, then someone should write that PEP :-) This could be Tim, or Barry who's been maintaining flufl.enum for a long time. If no one steps up, I will gladly do it.

Thoughts?

Eli

Antoine Pitrou

unread,
Feb 12, 2013, 10:31:02 AM2/12/13
to python...@python.org
Le Tue, 12 Feb 2013 07:21:04 -0800,
Eli Bendersky <eli...@gmail.com> a écrit :

>
> from enum import Enum
>
> class Color(Enum):
> RED, BLUE, GREEN

I think I'd favour a namedtuple-style approach, e.g.:

class Color(Enum):
fields = ('RED', 'BLUE', 'GREEN')

(which allows you to build enums programmatically too)

Also, I think you have to provide several classes, at least
SequenceEnum and StringEnum, and perhaps also BitmaskEnum.
It makes sense for them to be separate types, 1) because it makes it
explicit how the given enum behaves, 2) because combining integer and
str constants in an enum would be crazy.

Regards

Antoine.


_______________________________________________
Python-ideas mailing list
Python...@python.org
http://mail.python.org/mailman/listinfo/python-ideas

Ethan Furman

unread,
Feb 12, 2013, 10:31:42 AM2/12/13
to python...@python.org
On 02/12/2013 07:21 AM, Eli Bendersky wrote:
> While the parallel thread (and Tim's Bitbucket issue tracker) discusses
> his proposed implementation, I think it may be worthwhile to start from
> the other direction by writing a PEP that aims to include this in 3.4.
> The way I see it, the PEP should discuss and help us settle upon a
> minimal set of features deemed important in an enum.
>
> If that sounds OK to people, then someone should write that PEP :-) This
> could be Tim, or Barry who's been maintaining flufl.enum for a long
> time. If no one steps up, I will gladly do it.

A PEP would be excellent. I tried searching for one a couple days ago
to help my understanding with Tim's methods, but sadly didn't find one. :(

--
~Ethan~

Joao S. O. Bueno

unread,
Feb 12, 2013, 10:30:20 AM2/12/13
to Eli Bendersky, python-ideas
I'd be fine with writing the PEP as well - maybe we could do it together?


>
> Thoughts?
>
> Eli

Ethan Furman

unread,
Feb 12, 2013, 12:07:12 PM2/12/13
to python...@python.org
On 02/12/2013 07:31 AM, Antoine Pitrou wrote:
> Le Tue, 12 Feb 2013 07:21:04 -0800,
> Eli Bendersky <eli...@gmail.com> a écrit :
>>
>> from enum import Enum
>>
>> class Color(Enum):
>> RED, BLUE, GREEN
>
> I think I'd favour a namedtuple-style approach, e.g.:
>
> class Color(Enum):
> fields = ('RED', 'BLUE', 'GREEN')
>
> (which allows you to build enums programmatically too)
>
> Also, I think you have to provide several classes, at least
> SequenceEnum and StringEnum, and perhaps also BitmaskEnum.
> It makes sense for them to be separate types, 1) because it makes it
> explicit how the given enum behaves, 2) because combining integer and
> str constants in an enum would be crazy.

I partially agree with Antoine; there should be three types of Enum
available:

- sequence (most common: 0, 1, 2, 3, etc.)
- flag (1, 2, 4, 8, etc.)
- unique ('on', 'off'; 'north', 'south', 'east', 'west')

and they shouldn't be mixed together higgledy-piggledy in the same class.

However, I like the simplicity of Tim's approach.

The result of the implementation I've been playing with looks something
like:

class Color(Enum):
type = 'sequence'
RED, GREEN, BLUE

class Geometry(Enum):
type = 'unique'
LEFT, RIGHT, TOP, BOTTOM
WEST, EAST, NORTH, SOUTH

class FieldType(Enum):
type = 'flag'
BINARY, AUTOINC, NULLABLE


The metaclass takes care of assigning the next value and selecting the
proper subclass; 'unique' values are lower-cased (LEFT -> 'left');
subclassing is possible; and each Enum value is properly an instance of
it's Enum class.

One important reason for having the different types of Enum is for
prototyping.

--
~Ethan~

Joao S. O. Bueno

unread,
Feb 12, 2013, 1:05:44 PM2/12/13
to Antoine Pitrou, python...@python.org
On 12 February 2013 13:31, Antoine Pitrou <soli...@pitrou.net> wrote:
> Le Tue, 12 Feb 2013 07:21:04 -0800,
> Eli Bendersky <eli...@gmail.com> a écrit :
>>
>> from enum import Enum
>>
>> class Color(Enum):
>> RED, BLUE, GREEN
>
> I think I'd favour a namedtuple-style approach, e.g.:
>
> class Color(Enum):
> fields = ('RED', 'BLUE', 'GREEN')
>
> (which allows you to build enums programmatically too)

That is too much ' ' typing - I think it would be ok, to have
it like that, but a helper function that would work just like
the namedtuple call:
Color = Enum("Color", "RED GREEN BLUE", int)

The magic of
class Color(Enum):
RED, GREEN, BLUE

is tempting, but basically, what is required so that it does not break

class Color(Enum):
RED = other_module.value

(without making "other_module" a new enum value :-) ) if not worth it,
IMHO.

such magic is implemented in Tim's proof of concept.

A midle term could be to allow declarations like

class Color(IntEnum):
RED, BLUE, GREEN

but with no other logic or expressions in the class body -- all names
showing up are strictly part of the sequence, and them have a constructor call,
with keyword arguments for all the other cases.

>
> Also, I think you have to provide several classes, at least
> SequenceEnum and StringEnum, and perhaps also BitmaskEnum.
> It makes sense for them to be separate types, 1) because it makes it
> explicit how the given enum behaves, 2) because combining integer and
> str constants in an enum would be crazy.

+1

One other important point, that is missed somewhere along Tim's implementation:
the repr and str of the enum values should be the value's name - no need
to the fancy representation. Actually, the fancy representation defeats most
of the purpose of having the constants/enums

They could have a property like Enum._qualified_name if one needs it.

Guido van Rossum

unread,
Feb 12, 2013, 1:08:44 PM2/12/13
to Eli Bendersky, Python-Ideas
I'm torn. I like the clean look of Tim's:

class Flag(Enum):
RED, WHITE, BLUE

However, I don't like the magic needed for its implementation -- and
anybody who thinks they know Python pretty well and thinks about this
syntax for more than two seconds has to wonder how on earth it's done
(and the answer is pretty horrible). It is also pretty brittle to
depend on the lookup order -- and I suspect it will be prone to
masking other bugs (any typo in a name used in class-level code will
essentially generate a bogus new enum value).

OTOH I don't like having the string quotes of Antoine's counter-proposal:

class Flag(Enum):
fields = ('RED', 'WHITE', 'BLUE')

Whenever I see quoted names that are also used unquoted, I cringe a
little (and this includes calls to hasattr() or getattr() with a
string literal for the name).

The compromise that I would personally lean towards is more like this:

class Flag(Enum):
RED, WHITE, BLUE = <something>

Even if the <something> requires you to know how many values you are
defining, like range(3). If we have to have a somewhat more verbose
syntax that doesn't bother me too much.

FWIW I do like being able to define enums that are ints and strings
(but not mixed); masks/flags I see more as a special case of ints (if
you use this you are likely trying to match some API defined in C or
C++). I also agree that it must be possible to customize the enum
class and the behavior of the values by defining methods on the class.

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

Ethan Furman

unread,
Feb 12, 2013, 1:31:30 PM2/12/13
to python...@python.org
On 02/12/2013 10:08 AM, Guido van Rossum wrote:
> I'm torn. I like the clean look of Tim's:
>
> class Flag(Enum):
> RED, WHITE, BLUE
>
> However, I don't like the magic needed for its implementation -- and
> anybody who thinks they know Python pretty well and thinks about this
> syntax for more than two seconds has to wonder how on earth it's done
> (and the answer is pretty horrible). It is also pretty brittle to
> depend on the lookup order -- and I suspect it will be prone to
> masking other bugs (any typo in a name used in class-level code will
> essentially generate a bogus new enum value).

It seems to me the point of an enum is to give names to an order, so why
would the lookup order be a problem?

> OTOH I don't like having the string quotes of Antoine's counter-proposal:
>
> class Flag(Enum):
> fields = ('RED', 'WHITE', 'BLUE')
>
> The compromise that I would personally lean towards is more like this:
>
> class Flag(Enum):
> RED, WHITE, BLUE = <something>
>
> Even if the <something> requires you to know how many values you are
> defining, like range(3). If we have to have a somewhat more verbose
> syntax that doesn't bother me too much.


Having just dealt with using a poor-person's Enum (THIS, THAT, THE_OTHER
= range(3)) and then having to update it a few times (including all the
places in the code that depended on the length), I can safely say that
the range(n) construct is a PITA.

If you don't mind verbose, we could always go for something like:

class Color(Enum):
start_defs()
BLACK
RED, GREEN, BLUE
CYAN, YELLOW, MAGENTA
end_defs()

and if we mandate that the enum names must come first we can even ditch
the 'start_defs' call.

As far as typos go, I don't think that's a new problem (it certainly
isn't for me, anyway ;) and my best defense is plenty of tests.

--
~Ethan~

Guido van Rossum

unread,
Feb 12, 2013, 2:13:37 PM2/12/13
to Ethan Furman, Python-Ideas
On Tue, Feb 12, 2013 at 10:31 AM, Ethan Furman <et...@stoneleaf.us> wrote:
> On 02/12/2013 10:08 AM, Guido van Rossum wrote:
>>
>> I'm torn. I like the clean look of Tim's:
>>
>> class Flag(Enum):
>> RED, WHITE, BLUE
>>
>> However, I don't like the magic needed for its implementation -- and
>> anybody who thinks they know Python pretty well and thinks about this
>> syntax for more than two seconds has to wonder how on earth it's done
>> (and the answer is pretty horrible). It is also pretty brittle to
>> depend on the lookup order -- and I suspect it will be prone to
>> masking other bugs (any typo in a name used in class-level code will
>> essentially generate a bogus new enum value).
>
>
> It seems to me the point of an enum is to give names to an order, so why
> would the lookup order be a problem?

Because the language requirement that the expressions in a tuple are
evaluated left-to-right is fairly weak. (There are other, similar
contexts where the order is not l2r.)

>> OTOH I don't like having the string quotes of Antoine's counter-proposal:
>>
>> class Flag(Enum):
>> fields = ('RED', 'WHITE', 'BLUE')
>>
>> The compromise that I would personally lean towards is more like this:
>>
>> class Flag(Enum):
>> RED, WHITE, BLUE = <something>
>>
>> Even if the <something> requires you to know how many values you are
>> defining, like range(3). If we have to have a somewhat more verbose
>> syntax that doesn't bother me too much.
>
>
>
> Having just dealt with using a poor-person's Enum (THIS, THAT, THE_OTHER =
> range(3)) and then having to update it a few times (including all the places
> in the code that depended on the length), I can safely say that the range(n)
> construct is a PITA.
>
> If you don't mind verbose, we could always go for something like:
>
> class Color(Enum):
> start_defs()
> BLACK
> RED, GREEN, BLUE
> CYAN, YELLOW, MAGENTA
> end_defs()
>
> and if we mandate that the enum names must come first we can even ditch the
> 'start_defs' call.

If that's where you're leaning I would much rather do this:

class Color(Enum):
BLACK = val()
RED = val()
# etc.

EIBTI.

> As far as typos go, I don't think that's a new problem (it certainly isn't
> for me, anyway ;) and my best defense is plenty of tests.

So with Tim's implementation, what happens here:

class Color(Enum):
RED, GREEN, BLUE
if sys.platform == 'win32':
MAGENTA

and you happen to have no "import sys" in your module? The sys lookup
will succeed, create a new enum, and then you will get an error
something like this:

AttributeError: 'Enum' object has no attribute 'platform'

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

Barry Warsaw

unread,
Feb 12, 2013, 2:37:48 PM2/12/13
to python...@python.org
On Feb 12, 2013, at 04:31 PM, Antoine Pitrou wrote:

>(which allows you to build enums programmatically too)

I prefer an alternative API for creating enums programmatically, e.g.:

http://pythonhosted.org/flufl.enum/docs/using.html#alternative-api

-Barry
signature.asc

Barry Warsaw

unread,
Feb 12, 2013, 2:45:18 PM2/12/13
to python...@python.org
On Feb 12, 2013, at 09:07 AM, Ethan Furman wrote:

>The result of the implementation I've been playing with looks something like:
>
>class Color(Enum):
> type = 'sequence'
> RED, GREEN, BLUE
>
>class Geometry(Enum):
> type = 'unique'
> LEFT, RIGHT, TOP, BOTTOM
> WEST, EAST, NORTH, SOUTH
>
>class FieldType(Enum):
> type = 'flag'
> BINARY, AUTOINC, NULLABLE

IMHO, none of these approach the simplicity and explicitness of this API:

class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3

class FieldType(Enum):
BINARY = 1
AUTOINC = 2
NULLABLE = 4

The savings in a few characters, does not (again IMHO) make up for all the
magic and implicitness you have to guess at with the top versions.

I also don't find much value in the 'unique' style, which seem like they're
just another way to name string constants. Here though, you could argue that
the DRY principle at least makes them interesting, since IIUC, the explicit
alternative would be

class Geometry:
LEFT = 'left'
RIGHT = 'right'
TOP = 'top'
# ... and so on ...

I just wouldn't call these "enums" though. ;) Again, being a little more
explicit seems like a win, or at least not so bad.

Cheers,
-Barry
signature.asc

Barry Warsaw

unread,
Feb 12, 2013, 2:50:36 PM2/12/13
to python...@python.org
On Feb 12, 2013, at 04:05 PM, Joao S. O. Bueno wrote:

>That is too much ' ' typing - I think it would be ok, to have it like that,
>but a helper function that would work just like the namedtuple call: Color =
>Enum("Color", "RED GREEN BLUE", int)

How about:

>>> from flufl.enum import make
>>> make('Colors', 'RED GREEN BLUE'.split())
<Colors {RED: 1, GREEN: 2, BLUE: 3}>

-Barry
signature.asc

Guido van Rossum

unread,
Feb 12, 2013, 2:59:54 PM2/12/13
to Tim Delaney, Python-Ideas
After all the defenses I still don't like Tim's proposed syntax. Color me Barry.

On Tue, Feb 12, 2013 at 11:54 AM, Tim Delaney <tim.d...@aptare.com> wrote:
> On 13 February 2013 06:13, Guido van Rossum <gu...@python.org> wrote:
>>
>> So with Tim's implementation, what happens here:
>>
>> class Color(Enum):
>> RED, GREEN, BLUE
>> if sys.platform == 'win32':
>> MAGENTA
>>
>> and you happen to have no "import sys" in your module? The sys lookup
>> will succeed, create a new enum, and then you will get an error
>> something like this:
>>
>> AttributeError: 'Enum' object has no attribute 'platform'
>
>
> Well, that particular case would work (you'd get a NameError: sys) due to
> having done an attribute lookup on sys, but the following:
>
> class Color(Enum):
> RED, GREEN, BLUE
> if platfor == 'win32':
> MAGENTA
>
> would create an enum value 'platfor'.
>
> Personally, I don't think it's possible to have an enum with a simple
> interface and powerful semantics with just python code without it being
> fragile. I think I've got it fairly close, but there is horrible magic in
> there (multiple kinds) and there are definitely still edge cases. Any
> complete enum implementation is going to need some special handling by the
> parser I think.
>
> I'm actually thinking that to simplify things, I need a sentinel object to
> mark the end of the enum list (which allows other names after it). But that
> still wouldn't handle the case above (the if statement).
>
> BTW, for anyone who hasn't seen the magic code (largely uncommented, no
> explanations yet of how it's doing it - I probably won't get to that until
> the weekend) it's here: https://bitbucket.org/magao/enum
>
> Tim Delaney

Ethan Furman

unread,
Feb 12, 2013, 2:26:28 PM2/12/13
to Python-Ideas
On 02/12/2013 11:13 AM, Guido van Rossum wrote:
> On Tue, Feb 12, 2013 at 10:31 AM, Ethan Furman <et...@stoneleaf.us> wrote:
>> On 02/12/2013 10:08 AM, Guido van Rossum wrote:
>>>
>>> I'm torn. I like the clean look of Tim's:
>>>
>>> class Flag(Enum):
>>> RED, WHITE, BLUE
>>>
>>> However, I don't like the magic needed for its implementation -- and
>>> anybody who thinks they know Python pretty well and thinks about this
>>> syntax for more than two seconds has to wonder how on earth it's done
>>> (and the answer is pretty horrible). It is also pretty brittle to
>>> depend on the lookup order -- and I suspect it will be prone to
>>> masking other bugs (any typo in a name used in class-level code will
>>> essentially generate a bogus new enum value).
>>
>>
>> It seems to me the point of an enum is to give names to an order, so why
>> would the lookup order be a problem?
>
> Because the language requirement that the expressions in a tuple are
> evaluated left-to-right is fairly weak. (There are other, similar
> contexts where the order is not l2r.)

Ah.


>> If you don't mind verbose, we could always go for something like:
>>
>> class Color(Enum):
>> start_defs()
>> BLACK
>> RED, GREEN, BLUE
>> CYAN, YELLOW, MAGENTA
>> end_defs()
>>
>> and if we mandate that the enum names must come first we can even ditch the
>> 'start_defs' call.
>
> If that's where you're leaning I would much rather do this:
>
> class Color(Enum):
> BLACK = val()
> RED = val()
> # etc.
>
> EIBTI.

Personally, I'd be fine with:

class Color(Enum):
BLACK
RED
BLUE
PURPLE

which avoids the l2r issue, and the repetitive use of val() (or whatever
it's called).


>> As far as typos go, I don't think that's a new problem (it certainly isn't
>> for me, anyway ;) and my best defense is plenty of tests.
>
> So with Tim's implementation, what happens here:
>
> class Color(Enum):
> RED, GREEN, BLUE
> if sys.platform == 'win32':
> MAGENTA
>
> and you happen to have no "import sys" in your module? The sys lookup
> will succeed, create a new enum, and then you will get an error
> something like this:
>
> AttributeError: 'Enum' object has no attribute 'platform'

We could minimize that issue by requiring that enum names be ALLCAPS --
if something comes through that isn't, don't supply a value and let the
AttributeError perculate up.

--
~Ethan~

Tim Delaney

unread,
Feb 12, 2013, 3:13:21 PM2/12/13
to Python-Ideas
Sorry to anyone who gets this twice - sent it from my work address which is not subscribed to Python-Ideas.

On 13 February 2013 06:13, Guido van Rossum <gu...@python.org> wrote:
> As far as typos go, I don't think that's a new problem (it certainly isn't
> for me, anyway ;) and my best defense is plenty of tests.

So with Tim's implementation, what happens here:

class Color(Enum):
  RED, GREEN, BLUE
  if sys.platform == 'win32':
    MAGENTA

and you happen to have no "import sys" in your module? The sys lookup
will succeed, create a new enum, and then you will get an error
something like this:

AttributeError: 'Enum' object has no attribute 'platform'

Well, that particular case would work (you'd get a NameError: sys) due to having done an attribute lookup on sys, but the following:
class Color(Enum):
    RED, GREEN, BLUE

Ethan Furman

unread,
Feb 12, 2013, 2:58:30 PM2/12/13
to python...@python.org
On 02/12/2013 11:45 AM, Barry Warsaw wrote:
> On Feb 12, 2013, at 09:07 AM, Ethan Furman wrote:
>
>> The result of the implementation I've been playing with looks something like:
>>
>> class Color(Enum):
>> type = 'sequence'
>> RED, GREEN, BLUE
>>
>> class Geometry(Enum):
>> type = 'unique'
>> LEFT, RIGHT, TOP, BOTTOM
>> WEST, EAST, NORTH, SOUTH
>>
>> class FieldType(Enum):
>> type = 'flag'
>> BINARY, AUTOINC, NULLABLE
>
> IMHO, none of these approach the simplicity and explicitness of this API:
>
> class Color(Enum):
> RED = 1
> GREEN = 2
> BLUE = 3
>
> class FieldType(Enum):
> BINARY = 1
> AUTOINC = 2
> NULLABLE = 4
>
> The savings in a few characters, does not (again IMHO) make up for all the
> magic and implicitness you have to guess at with the top versions.

0) Once you read the docs you don't have to guess ;)
1) Magic is fun. :)
2) I hate repetition (= 1 = 2 = 3 is repetitive)
3) I make mistakes when repeating stuff (... = 11 = 11 = 13)


> I also don't find much value in the 'unique' style, which seem like they're
> just another way to name string constants. Here though, you could argue that
> the DRY principle at least makes them interesting, since IIUC, the explicit
> alternative would be
>
> class Geometry:
> LEFT = 'left'
> RIGHT = 'right'
> TOP = 'top'
> # ... and so on ...

Class membership can also be an advantage.

Joao S. O. Bueno

unread,
Feb 12, 2013, 3:33:13 PM2/12/13
to Barry Warsaw, python...@python.org
On 12 February 2013 17:50, Barry Warsaw <ba...@python.org> wrote:
>>That is too much ' ' typing - I think it would be ok, to have it like that,
>>but a helper function that would work just like the namedtuple call: Color =
>>Enum("Color", "RED GREEN BLUE", int)
>
> How about:
>
> >>> from flufl.enum import make
> >>> make('Colors', 'RED GREEN BLUE'.split())
> <Colors {RED: 1, GREEN: 2, BLUE: 3}>


Not bad - I think that wahtever is agreeded in a nice api that takes
advantage of the
"class" statement we should have a callabale constructor that takes
names as strings.

And moreover, it is nice to have a way to load the created
enums/constants to the current namespace -
and it can be done, without "magic" to the global namespace having a
call that takes a "namespace" argument
to which one would normally pass in blobals()

So:
from enum import load, make

would allow for:

>>> Colors = make("Colors", "RED GREEN BLUE".split())
>>> load(Colors, globals())
>>> RED
RED


(I think that allowing a call without the "split()" would be better - )
That would make some *linters complain - but those should update
themselves to the fact in time.

I consider a way to load the created constants into the module namespace
very important to the concept of enums/constant itself. Agreeing on a call
that can get "globals()" and update it would preclude the idea of being able
to do "from MyEnumClass import *" that showed up on the previous thread.

js
-><-

Yuval Greenfield

unread,
Feb 12, 2013, 3:38:21 PM2/12/13
to Ethan Furman, python-ideas
On Tue, Feb 12, 2013 at 9:58 PM, Ethan Furman <et...@stoneleaf.us> wrote:
1)  Magic is fun.  :)

I heartily agree. Check this out:

>>> RED, GREEN, BLUE = enum()
>>> print(RED, GREEN, BLUE)
0 1 2
>>> LEFT, TOP, RIGHT, DOWN = enum()
>>> print(LEFT, TOP, RIGHT, DOWN)
0 1 2 3


And here's the dirty trick....
 

import inspect
def enum():
    """If you use this  your computer will most likely burst ablaze and your teeth will fall off"""
    frame = inspect.stack()[1][0]
    # 4 = 3 for the function call "enum()", 1 for the opcode "UNPACK_SEQUENCE",
    # the next two bytes represent how many things we need to unpack.
    a, b = frame.f_code.co_code[frame.f_lasti + 4:frame.f_lasti + 6]
    enum_len = a + b * 256
    return range(enum_len)




Cheers,


Yuval

Joshua Landau

unread,
Feb 12, 2013, 3:43:50 PM2/12/13
to Ethan Furman, Python-Ideas
On 12 February 2013 19:26, Ethan Furman <et...@stoneleaf.us> wrote:
On 02/12/2013 11:13 AM, Guido van Rossum wrote:
On Tue, Feb 12, 2013 at 10:31 AM, Ethan Furman <et...@stoneleaf.us> wrote:
On 02/12/2013 10:08 AM, Guido van Rossum wrote:

I'm torn. I like the clean look of Tim's:

class Flag(Enum):
     RED, WHITE, BLUE

However, I don't like the magic needed for its implementation -- and
anybody who thinks they know Python pretty well and thinks about this
syntax for more than two seconds has to wonder how on earth it's done
(and the answer is pretty horrible). It is also pretty brittle to
depend on the lookup order -- and I suspect it will be prone to
masking other bugs (any typo in a name used in class-level code will
essentially generate a bogus new enum value).


It seems to me the point of an enum is to give names to an order, so why
would the lookup order be a problem?

Because the language requirement that the expressions in a tuple are
evaluated left-to-right is fairly weak. (There are other, similar
contexts where the order is not l2r.)
<snip>
Personally, I'd be fine with:

class Color(Enum):
   BLACK
   RED
   BLUE
   PURPLE

which avoids the l2r issue, and the repetitive use of val() (or whatever it's called).

Can you force people not to use:

class Color(Enum):
   BLACK, RED, BLUE, PURPLE

?
If I'm understanding the implementation, if you support one then you support the other. Hence the problem will still stand. That said, I'm not personally too worried about it.

As far as typos go, I don't think that's a new problem (it certainly isn't
for me, anyway ;) and my best defense is plenty of tests.

So with Tim's implementation, what happens here:

class Color(Enum):
   RED, GREEN, BLUE
   if sys.platform == 'win32':
     MAGENTA

and you happen to have no "import sys" in your module? The sys lookup
will succeed, create a new enum, and then you will get an error
something like this:

AttributeError: 'Enum' object has no attribute 'platform'

We could minimize that issue by requiring that enum names be ALLCAPS -- if something comes through that isn't, don't supply a value and let the AttributeError perculate up.

I think it's worse than that. I'm using Tim Delaney's code and I'm not really sure why it's doing what it's doing.

import sys

class Color(Enum):
   RED, GREEN, BLUE
   if sys.platform:
     MAGENTA

works and doesn't create Color.sys.

import sys

class Color(Enum):
   RED, GREEN, BLUE
   if sys:
     MAGENTA

works and _does_ create Color.sys.

So somehow there's enough magic to realise that if you are accessing an attribute, it's not going to be an enum. In other words, you don't need the ALLCAPS anyway. You might think "oh, good!". I think "oh noes!". It's way too magic for me.

The other idea:

class Color(Enum):
    values = "RED", "GREEN", "BLUE", "MAGENTA", "OTHER", "COLORS", "HERE", "TOO"

could be written

class Color(Enum):
    values = "RED GREEN BLUE MAGENTA OTHER COLORS HERE TOO".split()

so I'm OK with that, but then you're in the territory when you might as well go with the method Barry suggested.

Eli Bendersky

unread,
Feb 12, 2013, 3:58:44 PM2/12/13
to Guido van Rossum, Michael Foord, Python-Ideas, Tim Delaney
On Tue, Feb 12, 2013 at 11:59 AM, Guido van Rossum <gu...@python.org> wrote:
After all the defenses I still don't like Tim's proposed syntax. Color me Barry.

In this case, I see no reason not to use Barry's flufl.enum - it's pretty good and has been around for a while. The main advantage of Tim's implementation syntax (wasn't it Michael Foord who originally proposed it?) is that it lets us do less typing which is great. IOW, sure with flufl.enum I can do this and be reasonably happy:


class Color(Enum):
  RED = 1
  BLUE = 2
  GREEN = 3

But in 99% of the cases in Python code I absolutely don't care about the values of these enumerations (*), so I'd much, *much* rather write:

class Color(Enum):
  RED, BLUE, GREEN

Yes, the implementation is messy, but it can be documented and tested well, and it's fairly small and confined. But this is a matter of preference, and I can see your point (Guido) about preferring the more explicit syntax in order to avoid too much magic.

Eli

(*) And where I do care, I'd still enjoy them being generated automatically.


Antoine Pitrou

unread,
Feb 12, 2013, 4:04:12 PM2/12/13
to python...@python.org
On Tue, 12 Feb 2013 12:58:44 -0800
Eli Bendersky <eli...@gmail.com> wrote:

> On Tue, Feb 12, 2013 at 11:59 AM, Guido van Rossum <gu...@python.org> wrote:
>
> > After all the defenses I still don't like Tim's proposed syntax. Color me
> > Barry.
> >
>
> In this case, I see no reason not to use Barry's flufl.enum - it's pretty
> good and has been around for a while. The main advantage of Tim's
> implementation syntax (wasn't it Michael Foord who originally proposed it?)
> is that it lets us do less typing which is great. IOW, sure with flufl.enum
> I can do this and be reasonably happy:
>
> class Color(Enum):
> RED = 1
> BLUE = 2
> GREEN = 3

I still hope enum values are strings by default - or, if not, that they
get nice reprs. Integer enums are only useful for interoperability with
stubborn low-level libraries :-)

Regards

Antoine.

Tim Delaney

unread,
Feb 12, 2013, 4:14:51 PM2/12/13
to Python-Ideas, Michael Foord
On 13 February 2013 07:58, Eli Bendersky <eli...@gmail.com> wrote:
On Tue, Feb 12, 2013 at 11:59 AM, Guido van Rossum <gu...@python.org> wrote:
After all the defenses I still don't like Tim's proposed syntax. Color me Barry.

 The main advantage of Tim's implementation syntax (wasn't it Michael Foord who originally proposed it?) 

Indeed it was and I don't want to take any of the "credit" away from Michael. I simply took his original inspired idea and added several extra layers of magic on top ;) I've updated my README to make that clear.

Tim Delaney

dre...@gmail.com

unread,
Feb 12, 2013, 4:19:13 PM2/12/13
to gu...@python.org, python-ideas
Hello.

> masks/flags I see more as a special case of ints (if you use this you are likely trying to match some API defined in C or C++).

Comfortably encoding flags as int is just a half of FlagsEnum functionality. It has also abstract set semantics, see http://mail.python.org/pipermail/python-ideas/2013-January/019150.html .

Regards, Drekin

Tim Delaney

unread,
Feb 12, 2013, 4:19:41 PM2/12/13
to Python-Ideas, Michael Foord
On 13 February 2013 07:58, Eli Bendersky <eli...@gmail.com> wrote:

But in 99% of the cases in Python code I absolutely don't care about the values of these enumerations (*), so I'd much, *much* rather write:

class Color(Enum):
  RED, BLUE, GREEN

Yes, the implementation is messy, but it can be documented and tested well, and it's fairly small and confined. But this is a matter of preference, and I can see your point (Guido) about preferring the more explicit syntax in order to avoid too much magic.

Eli

(*) And where I do care, I'd still enjoy them being generated automatically.

I'm of the opinion that the most fundamental property of an enum is that it can generate its values automatically. Everything else is a nice-to-have, but without that property it's not much use as an enum.

Tim Delaney 

Guido van Rossum

unread,
Feb 12, 2013, 4:29:17 PM2/12/13
to Eli Bendersky, Python-Ideas, Tim Delaney, Michael Foord
Please. Anything *except*

class Color(Enum):
RED, BLUE, GREEN

And not this eiter:

class Color(Enum):
RED
BLUE
GREEN

Stefan Krah

unread,
Feb 12, 2013, 4:46:55 PM2/12/13
to python...@python.org
Guido van Rossum <gu...@python.org> wrote:
> Please. Anything *except*
>
> class Color(Enum):
> RED, BLUE, GREEN
>
> And not this eiter:
>
> class Color(Enum):
> RED
> BLUE
> GREEN

Is a new enum keyword still a possibility? To me that seems to be the
cleanest way.


Stefan Krah

ry...@ryanhiebert.com

unread,
Feb 12, 2013, 4:55:06 PM2/12/13
to python...@python.org
Forgive me, a lurker, but I wonder: how does an enum differ in usage from something like a Symbol from ruby, other than that an enum is required to be declared beforehand?

I'm definitely a python guy, but the idea of identifiers that are their own value interests me, and seems like it could have some overlap with the concept and usage of enums.

If there's not much difference in usage between enums and symbols apart from declaration, perhaps we could make the declaration optional and combine the concepts? This would certainly require parser changes.

Ryan

Guido van Rossum

unread,
Feb 12, 2013, 4:58:04 PM2/12/13
to ry...@ryanhiebert.com, python...@python.org
I don't know Ruby symbols, but if they are anything like Lisp symbols,
they are completely different. Lisp symbols are interned strings --
Python just uses strings (and if they happen to be interned you get a
modest speedup). This is for things like:

if hasattr(x, 'foo'):
x.foo()
--
--Guido van Rossum (python.org/~guido)

Guido van Rossum

unread,
Feb 12, 2013, 4:58:49 PM2/12/13
to Antoine Pitrou, Python-Ideas
Definitely they need nice reprs.
--
--Guido van Rossum (python.org/~guido)

Guido van Rossum

unread,
Feb 12, 2013, 4:59:44 PM2/12/13
to Tim Delaney, Python-Ideas, Michael Foord
And I'm of the opinion that the most fundamental property of an enum
is that it doesn't print as an int.

Tim Delaney

unread,
Feb 12, 2013, 5:09:04 PM2/12/13
to Python-Ideas, Guido van Rossum, Michael Foord
On 13 February 2013 08:59, Guido van Rossum <gu...@python.org> wrote:
And I'm of the opinion that the most fundamental property of an enum
is that it doesn't print as an int.

That part is easy.

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from enum import Enum
>>> class Color(Enum):
...     RED
...
>>> str(Color.RED)
'Color.RED'
>>> repr(Color.RED)
"<EnumValue 'Color.RED': 0>"
>>> str(Color)
'{RED:0}'
>>> repr(Color)
"<enum __main__.Color {<EnumValue 'RED': 0>}>"

Making an appropriate str/repr just requires deciding what it should be. I chose to make the reprs give more information, but there's no reason they couldn't be simplified.

I'll see if I can come up with a syntax for declaration that we're both happy with, but I doubt I'll be as happy with it as what I've currently got (despite its edge cases).

Tim Delaney 

ry...@ryanhiebert.com

unread,
Feb 12, 2013, 5:13:53 PM2/12/13
to Guido van Rossum, python...@python.org
Thanks for that. I think that when I consider the cases that I don't like the idea of using strings, using a proper enum makes better sense anyway.

Eli Bendersky

unread,
Feb 12, 2013, 5:13:57 PM2/12/13
to Antoine Pitrou, python-ideas
On Tue, Feb 12, 2013 at 1:04 PM, Antoine Pitrou <soli...@pitrou.net> wrote:
On Tue, 12 Feb 2013 12:58:44 -0800
Eli Bendersky <eli...@gmail.com> wrote:

> On Tue, Feb 12, 2013 at 11:59 AM, Guido van Rossum <gu...@python.org> wrote:
>
> > After all the defenses I still don't like Tim's proposed syntax. Color me
> > Barry.
> >
>
> In this case, I see no reason not to use Barry's flufl.enum - it's pretty
> good and has been around for a while. The main advantage of Tim's
> implementation syntax (wasn't it Michael Foord who originally proposed it?)
> is that it lets us do less typing which is great. IOW, sure with flufl.enum
> I can do this and be reasonably happy:
>
> class Color(Enum):
>   RED = 1
>   BLUE = 2
>   GREEN = 3

I still hope enum values are strings by default - or, if not, that they
get nice reprs. Integer enums are only useful for interoperability with
stubborn low-level libraries :-)

I agree, and Tim's implementation provides a very nice string representation, no matter what the value underneath is. I don't really care what the value underneath is, actually.

Eli

Ethan Furman

unread,
Feb 12, 2013, 5:14:22 PM2/12/13
to python-ideas
That is pretty cool.

Now all it needs is a nice repr(), and appropriate comparisons with only
int and same enum group.

Is it cross-Python?

Jonathan Slenders

unread,
Feb 12, 2013, 5:29:38 PM2/12/13
to Stefan Krah, python...@python.org


2013/2/12 Stefan Krah <ste...@bytereef.org>

Guido van Rossum <gu...@python.org> wrote:
> Please. Anything *except*
>
> class Color(Enum):
>   RED, BLUE, GREEN
>
> And not this eiter:
>
> class Color(Enum):
>   RED
>   BLUE
>   GREEN

Is a new enum keyword still a possibility? To me that seems to be the
cleanest way.

I was just thinking the same. We don't like to have more syntax than necessary, but we should also not introduce too much magic...



Tim Delaney

unread,
Feb 12, 2013, 5:32:25 PM2/12/13
to Python-Ideas
On 13 February 2013 09:29, Jonathan Slenders <jona...@slenders.be> wrote:
Is a new enum keyword still a possibility? To me that seems to be the
cleanest way.

I was just thinking the same. We don't like to have more syntax than necessary, but we should also not introduce too much magic...

That's my conclusion as well. To get a nice* enum with guaranteed semantics and no edge cases, I think it needs parser support.

* for my definition of "nice".

Tim Delaney

Steven D'Aprano

unread,
Feb 12, 2013, 5:51:50 PM2/12/13
to python...@python.org
On 13/02/13 06:45, Barry Warsaw wrote:
[...]
> IMHO, none of these approach the simplicity and explicitness of this API:
>
> class Color(Enum):
> RED = 1
> GREEN = 2
> BLUE = 3
>
> class FieldType(Enum):
> BINARY = 1
> AUTOINC = 2
> NULLABLE = 4
>
> The savings in a few characters, does not (again IMHO) make up for all the
> magic and implicitness you have to guess at with the top versions.
>
> I also don't find much value in the 'unique' style, which seem like they're
> just another way to name string constants. Here though, you could argue that
> the DRY principle at least makes them interesting, since IIUC, the explicit
> alternative would be
>
> class Geometry:
> LEFT = 'left'
> RIGHT = 'right'
> TOP = 'top'
> # ... and so on ...
>
> I just wouldn't call these "enums" though. ;) Again, being a little more
> explicit seems like a win, or at least not so bad.

My perspective from the peanut gallery is, not so. That "little more"
explicitness actually raises the barrier to using enums enough that they
are underused. In my experience, good practice or not, too people avoid
defining enum-like constants:

LEFT = 'left'
RIGHT = 'right'
...

and just embed the string literals in their code instead.

def move(distance, direction):
if direction == 'left':
...


It's hard to avoid the "magic constant" anti-pattern, and too easy to fall
into the bad habit.

Speaking as an ex-Pascal programmer, I really miss the simplicity of creating
enumerated types.

type
units = (inches, feet, furlongs, miles);
relationship = (parent, sibling, child);


which would correspond to Tim's suggestion:


class units(Enum):
INCHES, FEET, FURLONGS, MILES

class relationship(Enum):
PARENT, SIBLING, CHILD


I would use that, since I don't really care what the values of the enums are.
All I care is:

- they're unique within a type/class;
- they print like their name (e.g. FEET not 1);
- (optional) they can combine as bitmasks.


Not having to set their value explicitly is a *good thing*. If I don't know
the underlying value, I'm not tempted to do this:

some_func(42, unit=1) # should be unit=FEET


But if I had to use something like this:

class units(Enum):
INCHES = val()
FEET = val()
FURLONGS = val()
MILES = val()


or this:

class relationship(Enum):
PARENT, SIBLING, CHILD = range(3)


I'd probably wouldn't bother. I'd stick to "poor man's enum", like this:

PARENT, SIBLING, CHILD = range(3)

or (to my shame, but I have to be realistic) magic constants.



--
Steven

Markus Unterwaditzer

unread,
Feb 12, 2013, 5:50:01 PM2/12/13
to Guido van Rossum, Eli Bendersky, Python-Ideas
Guido van Rossum <gu...@python.org> wrote:

>I'm torn. I like the clean look of Tim's:
>
>class Flag(Enum):
> RED, WHITE, BLUE
>
>However, I don't like the magic needed for its implementation -- and
>anybody who thinks they know Python pretty well and thinks about this
>syntax for more than two seconds has to wonder how on earth it's done
>(and the answer is pretty horrible). It is also pretty brittle to
>depend on the lookup order -- and I suspect it will be prone to
>masking other bugs (any typo in a name used in class-level code will
>essentially generate a bogus new enum value).
>
>OTOH I don't like having the string quotes of Antoine's
>counter-proposal:
>
>class Flag(Enum):
> fields = ('RED', 'WHITE', 'BLUE')
>
>Whenever I see quoted names that are also used unquoted, I cringe a
>little (and this includes calls to hasattr() or getattr() with a
>string literal for the name).
>
>The compromise that I would personally lean towards is more like this:
>
>class Flag(Enum):
> RED, WHITE, BLUE = <something>
>
>Even if the <something> requires you to know how many values you are
>defining, like range(3). If we have to have a somewhat more verbose
>syntax that doesn't bother me too much.
>
>FWIW I do like being able to define enums that are ints and strings
>(but not mixed); masks/flags I see more as a special case of ints (if
>you use this you are likely trying to match some API defined in C or
>C++). I also agree that it must be possible to customize the enum
>class and the behavior of the values by defining methods on the class.
>
>--
>--Guido van Rossum (python.org/~guido)
>_______________________________________________
>Python-ideas mailing list
>Python...@python.org
>http://mail.python.org/mailman/listinfo/python-ideas

Forgive my naiveness, but why do we need a new type for enums? Wouldn't a new builtin function that returns a dict suffice? Something like:

def enum(*keys):
return dict((k, i) for i, k in enumerate(keys))

loglevels = enum('debug', 'warning', 'error')

-- Markus

Guido van Rossum

unread,
Feb 12, 2013, 5:56:45 PM2/12/13
to Steven D'Aprano, Python-Ideas
Frankly, enums are not that useful in small programs. For large
programs or libraries, and especially for public APIs, the extra cost
of defining the enum shouldn't count against them.

Let's just import Barry's enums into the stdlib.

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

Guido van Rossum

unread,
Feb 12, 2013, 5:58:15 PM2/12/13
to Markus Unterwaditzer, Python-Ideas
On Tue, Feb 12, 2013 at 2:50 PM, Markus Unterwaditzer
<mar...@unterwaditzer.net> wrote:
> Forgive my naiveness, but why do we need a new type for enums? Wouldn't a new builtin function that returns a dict suffice? Something like:
>
> def enum(*keys):
> return dict((k, i) for i, k in enumerate(keys))
>
> loglevels = enum('debug', 'warning', 'error')

Because you shouldn't have to put the enum names in quotes.

Steven D'Aprano

unread,
Feb 12, 2013, 6:00:38 PM2/12/13
to python...@python.org
On 13/02/13 08:04, Antoine Pitrou wrote:

> I still hope enum values are strings by default - or, if not, that they
> get nice reprs.

99.9% of the time I don't care what value enums have, in fact it is an
advantage if I don't know. Pascal, as I remember it, doesn't give you any
way to find out. But I consider enums displaying as their name to be
essential to an enum.



--
Steven

Jonathan Slenders

unread,
Feb 12, 2013, 6:01:41 PM2/12/13
to Tim Delaney, Python-Ideas
Just quickly thinking a little out of the box.

Suppose, that we added grammar for scheme-like symbols. (You know, these atomic identifiers, prefixed with a quote. I don't know much about scheme, but as I understand, they are just constants.)

In Python, the syntax could be similar, we could simply do in a global module scope:

sym symbol

which would translate to:

locals()['symbol'] = Symbol('symbol')

Where Symbol is a built-in that cannot change, which doesn't have a content, except for its name, and never equals anything but itself. From then on, we can access it through the name-binding 'symbol'

Then we could probably create enums, using the classes as we have:

class Colors:
    sym red
    sym green
    sym blue

There are probably some gaps here in my reasoning, but this could also work.

Cheers,
Jonathan



2013/2/12 Tim Delaney <timothy....@gmail.com>

Eli Bendersky

unread,
Feb 12, 2013, 6:06:36 PM2/12/13
to Guido van Rossum, Python-Ideas
On Tue, Feb 12, 2013 at 2:56 PM, Guido van Rossum <gu...@python.org> wrote:
Frankly, enums are not that useful in small programs. For large
programs or libraries, and especially for public APIs, the extra cost
of defining the enum shouldn't count against them.

Let's just import Barry's enums into the stdlib.

SGTM. So no need to write a PEP?

Eli


Steven D'Aprano

unread,
Feb 12, 2013, 6:08:19 PM2/12/13
to python...@python.org
On 13/02/13 06:50, Barry Warsaw wrote:
> On Feb 12, 2013, at 04:05 PM, Joao S. O. Bueno wrote:
>
>> That is too much ' ' typing - I think it would be ok, to have it like that,
>> but a helper function that would work just like the namedtuple call: Color =
>> Enum("Color", "RED GREEN BLUE", int)
>
> How about:
>
> >>> from flufl.enum import make
> >>> make('Colors', 'RED GREEN BLUE'.split())
> <Colors {RED: 1, GREEN: 2, BLUE: 3}>


That reminds me of namedtuple. I could live with something close to that.

Like namedtuple, it should do the split for you.



--
Steven

Tim Delaney

unread,
Feb 12, 2013, 6:09:05 PM2/12/13
to Guido van Rossum, Python-Ideas
On 13 February 2013 09:56, Guido van Rossum <gu...@python.org> wrote:
Frankly, enums are not that useful in small programs. For large
programs or libraries, and especially for public APIs, the extra cost
of defining the enum shouldn't count against them.

Let's just import Barry's enums into the stdlib.

That's entirely your call. FWIW I probably won't use them, as they fail to meet my needs for an enum, #1 being not having to specify their values in any way if I don't want to. Once you get past 3 or 4 values, it's too easy to miss or reuse a value.

Tim Delaney 

Steven D'Aprano

unread,
Feb 12, 2013, 6:09:39 PM2/12/13
to python...@python.org
On 13/02/13 07:38, Yuval Greenfield wrote:
> On Tue, Feb 12, 2013 at 9:58 PM, Ethan Furman<et...@stoneleaf.us> wrote:
>
>> 1) Magic is fun. :)
>>
>
> I heartily agree. Check this out:
>
>>>> RED, GREEN, BLUE = enum()
>>>> print(RED, GREEN, BLUE)
> 0 1 2
>>>> LEFT, TOP, RIGHT, DOWN = enum()
>>>> print(LEFT, TOP, RIGHT, DOWN)
> 0 1 2 3
>
>
> And here's the dirty trick....

[snip code]

Awesome!

:-)

Markus Unterwaditzer

unread,
Feb 12, 2013, 6:09:49 PM2/12/13
to Guido van Rossum, Python-Ideas
Guido van Rossum <gu...@python.org> wrote:

>On Tue, Feb 12, 2013 at 2:50 PM, Markus Unterwaditzer
><mar...@unterwaditzer.net> wrote:
>> Forgive my naiveness, but why do we need a new type for enums?
>Wouldn't a new builtin function that returns a dict suffice? Something
>like:
>>
>> def enum(*keys):
>> return dict((k, i) for i, k in enumerate(keys))
>>
>> loglevels = enum('debug', 'warning', 'error')
>
>Because you shouldn't have to put the enum names in quotes.

Oh, I thought you were just against quoted names if they were also accessible without those:

>Whenever I see quoted names that are also used unquoted, I cringe a little (and this includes calls to hasattr() or getattr() with a string literal for the name).
-- Markus

Guido van Rossum

unread,
Feb 12, 2013, 6:12:02 PM2/12/13
to Markus Unterwaditzer, Python-Ideas
Well but you will use them as loglevels.debug, right?

On Tue, Feb 12, 2013 at 3:09 PM, Markus Unterwaditzer
<mar...@unterwaditzer.net> wrote:
> Guido van Rossum <gu...@python.org> wrote:
>
>>On Tue, Feb 12, 2013 at 2:50 PM, Markus Unterwaditzer
>><mar...@unterwaditzer.net> wrote:
>>> Forgive my naiveness, but why do we need a new type for enums?
>>Wouldn't a new builtin function that returns a dict suffice? Something
>>like:
>>>
>>> def enum(*keys):
>>> return dict((k, i) for i, k in enumerate(keys))
>>>
>>> loglevels = enum('debug', 'warning', 'error')
>>
>>Because you shouldn't have to put the enum names in quotes.
>
> Oh, I thought you were just against quoted names if they were also accessible without those:
>
>>Whenever I see quoted names that are also used unquoted, I cringe a little (and this includes calls to hasattr() or getattr() with a string literal for the name).
> -- Markus



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

Ethan Furman

unread,
Feb 12, 2013, 6:14:29 PM2/12/13
to python...@python.org
On 02/12/2013 03:09 PM, Tim Delaney wrote:
> On 13 February 2013 09:56, Guido van Rossum wrote:
>
> Frankly, enums are not that useful in small programs. For large
> programs or libraries, and especially for public APIs, the extra cost
> of defining the enum shouldn't count against them.
>
> Let's just import Barry's enums into the stdlib.
>
>
> That's entirely your call. FWIW I probably won't use them, as they fail
> to meet my needs for an enum, #1 being not having to specify their
> values in any way if I don't want to. Once you get past 3 or 4 values,
> it's too easy to miss or reuse a value.

+1

Greg Ewing

unread,
Feb 12, 2013, 6:26:21 PM2/12/13
to python...@python.org
Steven D'Aprano wrote:

> 99.9% of the time I don't care what value enums have, in fact it is an
> advantage if I don't know. Pascal, as I remember it, doesn't give you any
> way to find out.

Yes, it does: the ord() function.

> But I consider enums displaying as their name to be
> essential to an enum.

That's one thing that, frustratingly, standard Pascal
*doesn't* give you (although many dialects have
extensions that do).

--
Greg

Georg Brandl

unread,
Feb 12, 2013, 6:57:38 PM2/12/13
to python...@python.org
Am 12.02.2013 20:45, schrieb Barry Warsaw:
> On Feb 12, 2013, at 09:07 AM, Ethan Furman wrote:
>
>>The result of the implementation I've been playing with looks something like:
>>
>>class Color(Enum):
>> type = 'sequence'
>> RED, GREEN, BLUE
>>
>>class Geometry(Enum):
>> type = 'unique'
>> LEFT, RIGHT, TOP, BOTTOM
>> WEST, EAST, NORTH, SOUTH
>>
>>class FieldType(Enum):
>> type = 'flag'
>> BINARY, AUTOINC, NULLABLE

I am also against this. It requires special support and heuristics in static
analysis tools in order not to flag these as errors. It will be annoying.

> IMHO, none of these approach the simplicity and explicitness of this API:
>
> class Color(Enum):
> RED = 1
> GREEN = 2
> BLUE = 3

We could even allow

class Color(Enum):
RED = 1
GREEN = ... # becomes 2
BLUE = ... # 3
MAGENTA = 5
FLUFL = ... # 6

class Color(FlagEnum):
RED = 1
GREEN = ... # becomes 2
BLUE = ... # 4
MAGENTA = 16
FLUFL = ...

class Color(StringEnum):
RED = ... # becomes 'red'
GREEN = ... # etc.
BLUE = ...

Georg

Georg Brandl

unread,
Feb 12, 2013, 6:59:08 PM2/12/13
to python...@python.org
Am 13.02.2013 00:08, schrieb Steven D'Aprano:
> On 13/02/13 06:50, Barry Warsaw wrote:
>> On Feb 12, 2013, at 04:05 PM, Joao S. O. Bueno wrote:
>>
>>> That is too much ' ' typing - I think it would be ok, to have it like that,
>>> but a helper function that would work just like the namedtuple call: Color =
>>> Enum("Color", "RED GREEN BLUE", int)
>>
>> How about:
>>
>> >>> from flufl.enum import make
>> >>> make('Colors', 'RED GREEN BLUE'.split())
>> <Colors {RED: 1, GREEN: 2, BLUE: 3}>
>
>
> That reminds me of namedtuple. I could live with something close to that.
>
> Like namedtuple, it should do the split for you.

And like namedtuple, it's ugly as hell :)

Georg

Mark Lawrence

unread,
Feb 12, 2013, 7:11:33 PM2/12/13
to python...@python.org
On 12/02/2013 22:32, Tim Delaney wrote:
>
> * for my definition of "nice".

Neurotic, Insecure, Crazy, Emotional? :)

>
> Tim Delaney
>

--
Cheers.

Mark Lawrence

Eric Snow

unread,
Feb 12, 2013, 8:07:31 PM2/12/13
to Georg Brandl, python-ideas
On Tue, Feb 12, 2013 at 4:57 PM, Georg Brandl <g.br...@gmx.net> wrote:
> We could even allow
>
> class Color(Enum):
> RED = 1
> GREEN = ... # becomes 2
> BLUE = ... # 3
> MAGENTA = 5
> FLUFL = ... # 6
>
> class Color(FlagEnum):
> RED = 1
> GREEN = ... # becomes 2
> BLUE = ... # 4
> MAGENTA = 16
> FLUFL = ...
>
> class Color(StringEnum):
> RED = ... # becomes 'red'
> GREEN = ... # etc.
> BLUE = ...

Nice. Both the use of ellipsis and having the semantics depend
directly on which specific class you inherit from.

At the same time, Steven D'Aprano's proposition of "the underlying
value is irrelevant" and "must be bitwise comparable" appeals to me.
If the underlying values matter then I'd call it a "constant" rather
than an "enum". A proper enum type would remove the temptation to use
the underlying value.

I would say that there is room for both an enum type and one or more
constants type (e.g. what Georg demonstrated above). Regardless, this
discussion would be well-served by recognizing the distinction.

-eric

Tim Delaney

unread,
Feb 12, 2013, 8:45:24 PM2/12/13
to Python-Ideas
On 13 February 2013 07:13, Tim Delaney <timothy....@gmail.com> wrote:
Well, that particular case would work (you'd get a NameError: sys) due to having done an attribute lookup on sys, but the following:

class Color(Enum):
    RED, GREEN, BLUE
    if platfor == 'win32':
        MAGENTA

would create an enum value 'platfor'.

BTW, I've just addressed this in my latest version up on BitBucket. Any usage of an object that is more than just assignment (basically, anything that results in a call of a __dunder__ method on the object) will now mark the name as not an enum value and result in a NameError for an unknown name.

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from enum import Enum
>>> class Color(Enum):
...     RED
...     if unknown_name == 'value':
...         PINK
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in Color
  File ".\enum.py", line 123, in __eq__
    def __eq__(self, other): return self._get(True) == other
  File ".\enum.py", line 98, in _get
    raise NameError(self.key)
NameError: unknown_name

Tim Delaney 

Greg Ewing

unread,
Feb 12, 2013, 6:19:16 PM2/12/13
to python...@python.org
Ethan Furman wrote:
> If you don't mind verbose, we could always go for something like:
>
> class Color(Enum):
> start_defs()
> BLACK
> RED, GREEN, BLUE
> CYAN, YELLOW, MAGENTA
> end_defs()

Or

class Color(Enum):
values(BLACK,
RED, GREEN, BLUE,
CYAN, YELLOW, MAGENTA)

where values() ignores its arguments and ends the
definition phase.

This would also provide a way to avoid relying on
language guarantees of evaluation order. The initial
lookup of the names could assign arbitrary values to
them, and then values() could remap them to sequential
values based on the order it receives them in.

--
Greg

Joao S. O. Bueno

unread,
Feb 12, 2013, 10:41:12 PM2/12/13
to Guido van Rossum, Tim Delaney, Python-Ideas, Michael Foord
On 12 February 2013 19:29, Guido van Rossum <gu...@python.org> wrote:
> Please. Anything *except*
>
> class Color(Enum):
> RED, BLUE, GREEN
>
> And not this eiter:
>
> class Color(Enum):
> RED
> BLUE
> GREEN

Just throwing in another syntax that is valid, requires no quotes and
no magic - in case someone
is interested in developing on it:

>>> Colors = make(RED=..., GREEN=..., BLUE=...)

Stephen J. Turnbull

unread,
Feb 12, 2013, 10:41:20 PM2/12/13
to Stefan Krah, python...@python.org
Stefan Krah writes:
> Guido van Rossum <gu...@python.org> wrote:
> > Please. Anything *except*
> >
> > class Color(Enum):
> > RED, BLUE, GREEN

I'll take you at your word. But maybe you should stop reading
now.<wink/>

> Is a new enum keyword still a possibility? To me that seems to be the
> cleanest way.

If parser support were on the table, couldn't we avoid a new keyword
by aping the Pascal syntax with

class Color(Enum) = [RED, GREEN, BLUE]

or something like that? I realize this is horrible because of the
missing colon, and because availability of the syntax depends on the
base class. Maybe it will suggest something to somebody, though. The
base class seems to be needed to support variants like OrderedEnum and
FlagEnum, as the implicit Enum

class Color = [RED, GREEN, BLUE]

can't disambiguate common use cases (ordered enums, or-able flags).
Another possibility would be to allow the list of identifiers as the
base class:

class Color([RED, GREEN, BLUE]):
pass

I suppose this is better than the version using "=", which doesn't
seem to allow explicit initialization of the enumerated ids, or
operations on them, in the class body. Perhaps the Enum base class
should be explicit:

class Color(Enum, [RED, GREEN, BLUE]):
pass

In all cases the point of using lists instead of (implicit) tuples is
to clearly guarantee order of evaluation to support OrderedEnum. Sets
are out because FlagEnum doesn't need such support.

Stephen J. Turnbull

unread,
Feb 12, 2013, 10:14:13 PM2/12/13
to Guido van Rossum, Python-Ideas
Guido van Rossum writes:

> Frankly, enums are not that useful in small programs.

This seems like a very personal thing to me. In some sense that I
suppose can be made as objective as counting keystrokes[1], I have to
agree with you. But I don't feel that Steven's cringing at "magic
literals" is a less valid argument for supporting a particular syntax
than your cringing at "quoted identifier names".

Of course, technically speaking, Steven could perfectly well use
"explicit-valued" enums[2] to eliminate magic literals. But from a
beautiful language perspective, ISTM that he is just one among many to
advocate that an enum *definition* should look like an *unordered list
of identifiers*.[3] It's not about keystroke count, it's about
correspondence of syntax to semantics. He's not doing himself any
favors by replacing a few magic literals with a hard to read,
overdefined enum declaration.

I strongly suspect that if you put explicit-valued enums into the
stdlib now, you will find that there remains a strong demand for
"nice" enums as Tim calls them (although most RFEs will probably use
the word "real"!)

Footnotes:
[1] But far more important, I grant you that. ;-)

[2] Note that EIBTI is *not* an argument for explicit-valued enums,
because an enum elements's name and class should tell you everything
you need to know about its semantics. If it doesn't, you aren't using
it as an enum! And "YELLOW = val()" is hardly explicit. :-(

[3] If that "list" of names actually has more structure (a set that
allows subsetting and union as commonly done with flags, or an order
such as the spectrum of colors), it would be nice if that structure is
imposed automatically as well -- but this should be done by a subclass
such as OrderedEnum or FlagEnum which makes the structure explicit.

Nick Coghlan

unread,
Feb 12, 2013, 11:07:48 PM2/12/13
to Tim Delaney, python...@python.org

What's wrong with enum.make? That just accepts the sequence of names namedtuple style, no values specified anywhere.

Cheers,
Nick.

>
> Tim Delaney 

Terry Reedy

unread,
Feb 13, 2013, 1:45:54 AM2/13/13
to python...@python.org
On 2/12/2013 3:43 PM, Joshua Landau wrote:

> class Color(Enum):
> values = "RED GREEN BLUE MAGENTA OTHER COLORS HERE TOO".split()
>
> so I'm OK with that, but then you're in the territory when you might as
> well go with the method Barry suggested.

A custom mapping can automatically do the split. This is an interesting
compromise between no quotes and multiple quotes.

--
Terry Jan Reedy

Eric Snow

unread,
Feb 13, 2013, 1:49:01 AM2/13/13
to python-ideas
On Tue, Feb 12, 2013 at 6:07 PM, Eric Snow <ericsnow...@gmail.com> wrote:
> At the same time, Steven D'Aprano's proposition of "the underlying
> value is irrelevant" and "must be bitwise comparable" appeals to me.
> If the underlying values matter then I'd call it a "constant" rather
> than an "enum". A proper enum type would remove the temptation to use
> the underlying value.

Here's an implementation of what I'm talking about:

http://code.activestate.com/recipes/578455/

Each enum value is simply an object and not backed by any underlying
value. The bitwise operators are implemented. The implementation is
a little rough, but hopefully gets the idea across.

Antoine Pitrou

unread,
Feb 13, 2013, 2:21:29 AM2/13/13
to python...@python.org
On Wed, 13 Feb 2013 00:57:38 +0100
Georg Brandl <g.br...@gmx.net> wrote:
> > IMHO, none of these approach the simplicity and explicitness of this API:
> >
> > class Color(Enum):
> > RED = 1
> > GREEN = 2
> > BLUE = 3
>
> We could even allow
>
> class Color(Enum):
> RED = 1
> GREEN = ... # becomes 2
> BLUE = ... # 3
> MAGENTA = 5
> FLUFL = ... # 6

The fact that the ellipsis variant requires *more* typing sounds a bit
wrong to me. The common case should be the easiest to type.

Regards

Antoine.

Georg Brandl

unread,
Feb 13, 2013, 2:34:21 AM2/13/13
to python...@python.org
Am 13.02.2013 08:21, schrieb Antoine Pitrou:
> On Wed, 13 Feb 2013 00:57:38 +0100
> Georg Brandl <g.br...@gmx.net> wrote:
>> > IMHO, none of these approach the simplicity and explicitness of this API:
>> >
>> > class Color(Enum):
>> > RED = 1
>> > GREEN = 2
>> > BLUE = 3
>>
>> We could even allow
>>
>> class Color(Enum):
>> RED = 1
>> GREEN = ... # becomes 2
>> BLUE = ... # 3
>> MAGENTA = 5
>> FLUFL = ... # 6
>
> The fact that the ellipsis variant requires *more* typing sounds a bit
> wrong to me. The common case should be the easiest to type.

Well, nobody forbids you from writing the integers out. But one of the
complaints was that you shouldn't need to know or write the constant
values (and "..." is shorter than "val()" which was proposed for that
purpose).

And for stringy enums that argument is void :)

Georg

Antoine Pitrou

unread,
Feb 13, 2013, 2:38:43 AM2/13/13
to python...@python.org
On Wed, 13 Feb 2013 08:34:21 +0100
Georg Brandl <g.br...@gmx.net> wrote:
> Am 13.02.2013 08:21, schrieb Antoine Pitrou:
> > On Wed, 13 Feb 2013 00:57:38 +0100
> > Georg Brandl <g.br...@gmx.net> wrote:
> >> > IMHO, none of these approach the simplicity and explicitness of this API:
> >> >
> >> > class Color(Enum):
> >> > RED = 1
> >> > GREEN = 2
> >> > BLUE = 3
> >>
> >> We could even allow
> >>
> >> class Color(Enum):
> >> RED = 1
> >> GREEN = ... # becomes 2
> >> BLUE = ... # 3
> >> MAGENTA = 5
> >> FLUFL = ... # 6
> >
> > The fact that the ellipsis variant requires *more* typing sounds a bit
> > wrong to me. The common case should be the easiest to type.
>
> Well, nobody forbids you from writing the integers out. But one of the
> complaints was that you shouldn't need to know or write the constant
> values (and "..." is shorter than "val()" which was proposed for that
> purpose).

It would still be shorter to type something like:

class Color(Enum):
values = ('RED', 'GREEN', 'BLUE', 'MAGENTA', 'FLUFL')

> And for stringy enums that argument is void :)

I'm not sure how it's void. What is the proposed syntax for string
enums?

Regards

Antoine.

Brett Cannon

unread,
Feb 13, 2013, 11:26:26 AM2/13/13
to Nick Coghlan, python-ideas
On Tue, Feb 12, 2013 at 11:07 PM, Nick Coghlan <ncog...@gmail.com> wrote:


On 13 Feb 2013 09:11, "Tim Delaney" <timothy....@gmail.com> wrote:
>
> On 13 February 2013 09:56, Guido van Rossum <gu...@python.org> wrote:
>>
>> Frankly, enums are not that useful in small programs. For large
>> programs or libraries, and especially for public APIs, the extra cost
>> of defining the enum shouldn't count against them.
>>
>> Let's just import Barry's enums into the stdlib.
>
>
> That's entirely your call. FWIW I probably won't use them, as they fail to meet my needs for an enum, #1 being not having to specify their values in any way if I don't want to. Once you get past 3 or 4 values, it's too easy to miss or reuse a value.

What's wrong with enum.make? That just accepts the sequence of names namedtuple style, no values specified anywhere.


Georg called it ugly, but that's the only complaint I remember reading. I too find them ugly but not enough to not say this isn't the best solution considering everyone's complaints of verbosity (e.g. Guido not liking excessive quoting), magic (e.g. all non-explicit proposals that don't involve calling a classmethod like val()), and conciseness (e.g. not having to specify every value).

Heck, if it was me I would just do::

  import types

  def make_enum(keys, *, value=None):
    """For incremental numbers: ``lambda key, index: index``.
    For bitwise flags: ``lambda key, index: 2**index``.

    Could change to take an iterator instead of a lambda and simply special-case the `None` argument for strings,
    but that seems needlessly limiting when the integer case is probably for interfacing with some C code.
    """
    if value is None:
      value = lambda key, index: key
    items = {}
    for key, index in enumerate(keys.split()):
      items[key] = value(key, index)
    return types.SimpleNamespace(**items)  # Creating a read-only subclass is trivial

But I know people will want fancier reprs, globally unique objects, etc. instead of just some simple way to create an object with attributes referencing automatically-generated, unique, predictable values (which is all I would want from an enum).

Antoine Pitrou

unread,
Feb 13, 2013, 11:38:07 AM2/13/13
to python...@python.org
Le Wed, 13 Feb 2013 14:07:48 +1000,
Nick Coghlan <ncog...@gmail.com> a
écrit :

> On 13 Feb 2013 09:11, "Tim Delaney"
> <timothy....@gmail.com> wrote:
> >
> > On 13 February 2013 09:56, Guido van Rossum
> > <gu...@python.org> wrote:
> >>
> >> Frankly, enums are not that useful in small programs. For large
> >> programs or libraries, and especially for public APIs, the extra
> >> cost of defining the enum shouldn't count against them.
> >>
> >> Let's just import Barry's enums into the stdlib.
> >
> >
> > That's entirely your call. FWIW I probably won't use them, as they
> > fail
> to meet my needs for an enum, #1 being not having to specify their
> values in any way if I don't want to. Once you get past 3 or 4
> values, it's too easy to miss or reuse a value.
>
> What's wrong with enum.make? That just accepts the sequence of names
> namedtuple style, no values specified anywhere.

What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
different idioms for declaring enums, while a single class-based
definition style should suffice.

Regards

Antoine.

Brett Cannon

unread,
Feb 13, 2013, 12:09:19 PM2/13/13
to Antoine Pitrou, python-ideas
On Wed, Feb 13, 2013 at 11:38 AM, Antoine Pitrou <soli...@pitrou.net> wrote:
Le Wed, 13 Feb 2013 14:07:48 +1000,
Nick Coghlan <ncog...@gmail.com> a
écrit :
> On 13 Feb 2013 09:11, "Tim Delaney"
> <timothy....@gmail.com> wrote:
> >
> > On 13 February 2013 09:56, Guido van Rossum
> > <gu...@python.org> wrote:
> >>
> >> Frankly, enums are not that useful in small programs. For large
> >> programs or libraries, and especially for public APIs, the extra
> >> cost of defining the enum shouldn't count against them.
> >>
> >> Let's just import Barry's enums into the stdlib.
> >
> >
> > That's entirely your call. FWIW I probably won't use them, as they
> > fail
> to meet my needs for an enum, #1 being not having to specify their
> values in any way if I don't want to. Once you get past 3 or 4
> values, it's too easy to miss or reuse a value.
>
> What's wrong with enum.make? That just accepts the sequence of names
> namedtuple style, no values specified anywhere.

What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
different idioms for declaring enums, while a single class-based
definition style should suffice.

Or just a function idiom. We don't have to support the class-based definition style either. 

Yuval Greenfield

unread,
Feb 13, 2013, 12:33:00 PM2/13/13
to Brett Cannon, Antoine Pitrou, python-ideas
On Wed, Feb 13, 2013 at 7:09 PM, Brett Cannon <br...@python.org> wrote:
Or just a function idiom. We don't have to support the class-based definition style either. 

Well, namedtuple has an enum for tuple indexes. I think we should be consistent which entails:

Colors = collections.enum('RED GREEN BLUE')


Cheers,

Yuval

Barry Warsaw

unread,
Feb 13, 2013, 1:00:33 PM2/13/13
to python...@python.org
On Feb 13, 2013, at 12:57 AM, Georg Brandl wrote:

>class Color(Enum):
> RED = 1
> GREEN = ... # becomes 2
> BLUE = ... # 3
> MAGENTA = 5
> FLUFL = ... # 6

That's pretty cute.

>class Color(FlagEnum):
> RED = 1
> GREEN = ... # becomes 2
> BLUE = ... # 4
> MAGENTA = 16
> FLUFL = ...
>
>class Color(StringEnum):
> RED = ... # becomes 'red'
> GREEN = ... # etc.
> BLUE = ...

I want to think about the latter two (especially the string one), but I've
filed this bug to track the feature.

https://bugs.launchpad.net/flufl.enum/+bug/1124357

Cheers,
-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 1:04:29 PM2/13/13
to python...@python.org
On Feb 13, 2013, at 10:08 AM, Steven D'Aprano wrote:

>> >>> from flufl.enum import make
>> >>> make('Colors', 'RED GREEN BLUE'.split())
>> <Colors {RED: 1, GREEN: 2, BLUE: 3}>
>
>That reminds me of namedtuple. I could live with something close to that.
>
>Like namedtuple, it should do the split for you.

Part of the reason for defining make() the way it is, is so that you can use
any iterator in the second argument. I suppose you could special case
splitting if the second argument is a single string, but I'm not sure this
special case is special enough.

-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 1:05:17 PM2/13/13
to python...@python.org
On Feb 13, 2013, at 12:59 AM, Georg Brandl wrote:

>>> >>> from flufl.enum import make
>>> >>> make('Colors', 'RED GREEN BLUE'.split())
>>> <Colors {RED: 1, GREEN: 2, BLUE: 3}>

>And like namedtuple, it's ugly as hell :)

Suggestions for a better repr are welcome. ;)

-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 1:14:03 PM2/13/13
to python...@python.org
On Feb 12, 2013, at 02:13 PM, Eli Bendersky wrote:

>I agree, and Tim's implementation provides a very nice string
>representation, no matter what the value underneath is. I don't really care
>what the value underneath is, actually.

Except that sometimes you do, which is why flufl.enum has a nice concise str
and a more detailed repr. The former doesn't include the underlying value
while the latter does. Edible cake.

-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 1:19:14 PM2/13/13
to python...@python.org
On Feb 13, 2013, at 08:19 AM, Tim Delaney wrote:

>I'm of the opinion that the most fundamental property of an enum is that it
>can generate its values automatically. Everything else is a nice-to-have,
>but without that property it's not much use as an enum.

See, I don't care about that much because I define an enum once, but I *use*
it hundreds of times. Thus a little more verbosity/explicitness at definition
time is a one-time cost, and a very low one at that. It might not seems that
way when you're writing them in so many mailing list replies though ;).

In real code, having a really good str and repr, along with good semantics
makes them much more useful for debugging, and very pleasant to use.

Cheers,
-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 1:12:39 PM2/13/13
to python...@python.org
On Feb 12, 2013, at 10:04 PM, Antoine Pitrou wrote:

>I still hope enum values are strings by default - or, if not, that they
>get nice reprs. Integer enums are only useful for interoperability with
>stubborn low-level libraries :-)

>>> from flufl.enum import make
>>> Colors = make('Colors', 'RED GREEN BLUE'.split())
>>> print(Colors.RED)
Colors.RED
>>> Colors.RED
<EnumValue: Colors.RED [int=1]>

Having the enum name in the str and (admittedly uglier but more informational)
repr is very important to me, because I typically use enums like so:

from mailman.interfaces.members import DeliveryMode
...
def send_plaintext(member):
...
if member.delivery_mode is DeliveryMode.regular:
...
elif member.delivery_mode is DeliveryMode.mime_digests:
...

Now, if something goes wrong, I can set a breakpoint or debug-print right
before the conditional and get the value of `member.delivery_mode`. The fact
that it will print `DeliveryMode.regular` makes debugging the problem a *much*
better experience.

Cheers,
-Barry
signature.asc

Tim Delaney

unread,
Feb 13, 2013, 2:22:11 PM2/13/13
to Barry Warsaw, Python-Ideas
Yes - my enum's exactly the same - simple, concise str (for both the enum and values) and detailed repr. The actual repr is a little different to flufl.enum repr (I think the str is the same) but conveys the same information.

Color.RED
<EnumValue 'Color.RED': 0>

{RED:0, GREEN:1, BLUE:2, CYAN:10, MAGENTA:11, YELLOW:12, BLACK:127}
<enum __main__.Color {<EnumValue 'RED': 0>, <EnumValue 'GREEN': 1>, <EnumValue 'BLUE': 2>, <EnumValue 'CYAN': 10>, <EnumValue 'MAGENTA': 11>, <EnumValue 'YELLOW': 12>, <EnumValue 'BLACK': 127>}>

Tim Delaney 

Tim Delaney

unread,
Feb 13, 2013, 2:28:37 PM2/13/13
to Barry Warsaw, Python-Ideas
On 14 February 2013 05:19, Barry Warsaw <ba...@python.org> wrote:
On Feb 13, 2013, at 08:19 AM, Tim Delaney wrote:

>I'm of the opinion that the most fundamental property of an enum is that it
>can generate its values automatically. Everything else is a nice-to-have,
>but without that property it's not much use as an enum.

See, I don't care about that much because I define an enum once, but I *use*
it hundreds of times.  Thus a little more verbosity/explicitness at definition
time is a one-time cost, and a very low one at that.  It might not seems that
way when you're writing them in so many mailing list replies though ;).

My main concern regarding this is ensuring that:

1. Values don't get repeated.

2. Values don't get skipped (unless explicitly skipped);

#1 obviously is the most important property, but in practice I find #2 to be very important as well. Whilst it's more verbose, the ... syntax isn't bad, and I've added support to it to my enum to play with. Currently my enum implementation includes the kitchen sink, but I'm looking to do some renovations soon.
 
In real code, having a really good str and repr, along with good semantics
makes them much more useful for debugging, and very pleasant to use.

Absolutely. But it's not just that either. If you can convert from the string name to the enum, it makes the enum suitable as a transport mechanism. Each end can have different values for the enum in practice so long as the names don't change - it can make things much more robust across versions.

Tim Delaney 

Antoine Pitrou

unread,
Feb 13, 2013, 2:30:05 PM2/13/13
to python...@python.org
On Wed, 13 Feb 2013 13:19:14 -0500
Barry Warsaw <ba...@python.org> wrote:
> On Feb 13, 2013, at 08:19 AM, Tim Delaney wrote:
>
> >I'm of the opinion that the most fundamental property of an enum is that it
> >can generate its values automatically. Everything else is a nice-to-have,
> >but without that property it's not much use as an enum.
>
> See, I don't care about that much because I define an enum once, but I *use*
> it hundreds of times. Thus a little more verbosity/explicitness at definition
> time is a one-time cost, and a very low one at that.

The cost is not low when you have many values. Also, with many values
and one value per line, it can make your declaration very long
vertically.

And it's not always true that you use an enum much more often than you
define it. For example, you may define many error codes (e.g. errnos)
for compatibility with another system, but only check a few of them
explicitly in your application code.

Antoine Pitrou

unread,
Feb 13, 2013, 2:27:31 PM2/13/13
to python...@python.org
It's not about the repr, it's about the make(...) call.

Jonathan Slenders

unread,
Feb 13, 2013, 3:48:13 PM2/13/13
to Barry Warsaw, python...@python.org
How is this a bug? It can be implemented in Python 3, I think. Only
Enum.__metaclass__ would not be able to know the order of it's
elements. (But unless you want to do bitwise operations, it's
irrelevant. Is alphabetical order okay?)


2013/2/13 Barry Warsaw <ba...@python.org>

Barry Warsaw

unread,
Feb 13, 2013, 4:57:53 PM2/13/13
to python...@python.org
On Feb 13, 2013, at 09:48 PM, Jonathan Slenders wrote:

>How is this a bug?

It's just an item in flufl.enum's bug tracker. It's marked "wish list" so
it's more correct to call it a feature request.

-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 5:00:39 PM2/13/13
to python...@python.org
On Feb 14, 2013, at 06:22 AM, Tim Delaney wrote:

>Yes - my enum's exactly the same - simple, concise str (for both the enum
>and values) and detailed repr. The actual repr is a little different to
>flufl.enum repr (I think the str is the same) but conveys the same
>information.

I like that you came to the same conclusion, namely that the str should be
simple and the repr more detailed. The exactly color of that detail can be
bikeshedded to death now. :)

-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 5:25:49 PM2/13/13
to python...@python.org
On Feb 14, 2013, at 06:28 AM, Tim Delaney wrote:

>1. Values don't get repeated.

This is easy, and flufl.enum guarantees this, even for subclasses:

>>> class Colors(Enum):
... red = 1
... blue = 1
...
Traceback (most recent call last):
...
TypeError: Multiple enum values: 1
>>> class Colors(Enum):
... red = 1
... blue = 2
...
>>> class MoreColors(Colors):
... green = 2
...
Traceback (most recent call last):
...
TypeError: Multiple enum values: 2

>2. Values don't get skipped (unless explicitly skipped);

Is #2 is the reason why you have different subclasses for flag enums, since
the definition of "skipped" is different?

Many folks have said they don't care about the actual enum values, so for
them, skips don't matter. Setting aside explicit skips, you can add such
verification with a class decorator, e.g.:

-----snip snip-----
from flufl.enum import Enum

def noskips(cls):
for i, intval in enumerate(cls, start=1):
if i != int(intval):
raise TypeError('Skipped value: {}'.format(i))
return cls

@noskips
class GoodColors(Enum):
green = 3
blue = 1
red = 2

@noskips
class BadColors(Enum):
green = 4
blue = 1
red = 2

$ python3 noskips.py
Traceback (most recent call last):
...
TypeError: Skipped value: 3
-----snip snip-----

>If you can convert from the string name to the enum, it makes the enum
>suitable as a transport mechanism.

>>> from flufl.enum import make
>>> Colors = make('Colors', 'red green blue'.split())
>>> Colors['red']
<EnumValue: Colors.red [int=1]>
>>> Colors[2]
<EnumValue: Colors.green [int=2]>
>>> Colors[Colors.green.name]
<EnumValue: Colors.green [int=2]>

Cheers,
-Barry
signature.asc

Barry Warsaw

unread,
Feb 13, 2013, 5:28:09 PM2/13/13
to python...@python.org
On Feb 13, 2013, at 08:30 PM, Antoine Pitrou wrote:

>The cost is not low when you have many values. Also, with many values
>and one value per line, it can make your declaration very long
>vertically.
>
>And it's not always true that you use an enum much more often than you
>define it. For example, you may define many error codes (e.g. errnos)
>for compatibility with another system, but only check a few of them
>explicitly in your application code.

Huge enums haven't been common in my experience, but in that case I'd probably
just use the make() helper.

-Barry
signature.asc

Nick Coghlan

unread,
Feb 13, 2013, 6:11:05 PM2/13/13
to Antoine Pitrou, python...@python.org


On 14 Feb 2013 02:39, "Antoine Pitrou" <soli...@pitrou.net> wrote:
>
> Le Wed, 13 Feb 2013 14:07:48 +1000,
> Nick Coghlan <ncog...@gmail.com> a
> écrit :
> > On 13 Feb 2013 09:11, "Tim Delaney"
> > <timothy....@gmail.com> wrote:
> > >
> > > On 13 February 2013 09:56, Guido van Rossum
> > > <gu...@python.org> wrote:
> > >>
> > >> Frankly, enums are not that useful in small programs. For large
> > >> programs or libraries, and especially for public APIs, the extra
> > >> cost of defining the enum shouldn't count against them.
> > >>
> > >> Let's just import Barry's enums into the stdlib.
> > >
> > >
> > > That's entirely your call. FWIW I probably won't use them, as they
> > > fail
> > to meet my needs for an enum, #1 being not having to specify their
> > values in any way if I don't want to. Once you get past 3 or 4
> > values, it's too easy to miss or reuse a value.
> >
> > What's wrong with enum.make? That just accepts the sequence of names
> > namedtuple style, no values specified anywhere.
>
> What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
> different idioms for declaring enums, while a single class-based
> definition style should suffice.

Really? What happened to the principle of layered API complexity, where a simple *convenience* function is the obvious way to do it for the cases it covers, while the full, more powerful, but also more verbose, explicit subclassing API covers the more complex cases?

Magic namespaces and metaclasses sure don't meet *my* definition of obvious. A normal subclassing API with a convenience function for simple cases? That's just good layered API design.

Cheers,
Nick.

Steven D'Aprano

unread,
Feb 13, 2013, 6:48:23 PM2/13/13
to python...@python.org
On 14/02/13 03:38, Antoine Pitrou wrote:
> Le Wed, 13 Feb 2013 14:07:48 +1000,
> Nick Coghlan<ncog...@gmail.com> a écrit :
[...]

>> What's wrong with enum.make? That just accepts the sequence of names
>> namedtuple style, no values specified anywhere.
>
> What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
> different idioms for declaring enums, while a single class-based
> definition style should suffice.

I disagree. enum.make is the more obvious solution, less verbose and IMO nicer looking too. It's explicit that it makes enums, the API is familiar to anyone who has used namedtuple, and it's an expression rather than a statement so it's more flexible. You can't do this with the class-based syntax:

[enum.make(name % i, factory()) for (i, factory) in enumerate(factories)]


Besides, the presence of a second, non-obvious solution is not a violation of One Obvious Way.

I find it amusing that we as a community put so much emphasis on the Zen which ironically includes one of Tim Peter's subtle jokes.

http://bugs.python.org/issue3364

Python is not Perl, but really, there's hardly anything in Python that can't be done two ways if you really try.

--
Steven

Paul Moore

unread,
Feb 13, 2013, 6:54:51 PM2/13/13
to Steven D'Aprano, python...@python.org
On 13 February 2013 23:48, Steven D'Aprano <st...@pearwood.info> wrote:
> I find it amusing that we as a community put so much emphasis on the Zen
> which ironically includes one of Tim Peter's subtle jokes.
>
> http://bugs.python.org/issue3364

Thanks for that link - I too had missed that joke.

Missing-Tim-even-more-now-ly y'rs
Paul.

Jan Kaliszewski

unread,
Feb 13, 2013, 7:46:05 PM2/13/13
to python...@python.org
13.02.2013 08:38, Antoine Pitrou wrote:

> It would still be shorter to type something like:
>
> class Color(Enum):
> values = ('RED', 'GREEN', 'BLUE', 'MAGENTA', 'FLUFL')

Or even in a namedtuple-like manner:

class Color(Enum):
values = 'RED, GREEN, BLUE, MAGENTA, FLUFL'

Cheers.
*j

Jan Kaliszewski

unread,
Feb 13, 2013, 7:57:50 PM2/13/13
to python...@python.org
13.02.2013 00:57, Georg Brandl wrote:

> We could even allow
>
> class Color(Enum):
> RED = 1
> GREEN = ... # becomes 2
> BLUE = ... # 3
> MAGENTA = 5
> FLUFL = ... # 6
>
> class Color(FlagEnum):
> RED = 1
> GREEN = ... # becomes 2
> BLUE = ... # 4
> MAGENTA = 16
> FLUFL = ...
>
> class Color(StringEnum):
> RED = ... # becomes 'red'
> GREEN = ... # etc.
> BLUE = ...

It's nice. But what about synonymous items?

class Color(Enum):
RED = R = 1
GREEN = ...
BLUE = B = ... # we ment BLUE = B = 3, but effectively
# we'll have BLUE = 3 and B = 4 :-|

What about:

(all comments are for explanation only)

class Color(Enum):
RED = 1 # 1
GREEN = +one # 2
BLUE = +one # 3
YELLOW = Y = +one # 4
BLACK = B = 10 # 10
ORANGE = -one # 9
VIOLET = -one # 8

class Flag(Enum):
FOO = 1 # 1
BAR = +rot # 2
BAZ = +rot # 4
BOO = +rot # 8
SPAM = 256 # 256
HAM = -rot # 128
RAM = -rot # 64

class Color(Enum):
RED = ... # 'RED'
GREEN = ... # 'GREEN'
BLUE = ... # 'BLUE'

and maybe also:

class Color(Enum):
# 0 1 2 3
RED, GREEN, BLUE, YELLOW, *end = seq()

class Color(Enum):
# 3 4 5 6
RED, GREEN, BLUE, YELLOW, *end = seq(3)

class Flag(Enum):
# 1 2 4 8 16
FOO, BAR, BAZ, BOO, SPAM, *end = flags()

(yes, it *is* possible to implement it without playing with stack
frames...)


Cheers.
*j


PS. And now for something completely different: :-)

@enum
def Color(v):
v.RED # 0
v.GREEN # 1
v.BLUE # 2
v.YELLOW # 3

@enum
def Color(v):
v.RED = 7
v.GREEN # 8
v.BLUE # 9
v.YELLOW # 10

Ryan Hiebert

unread,
Feb 13, 2013, 8:58:57 PM2/13/13
to Jan Kaliszewski, python...@python.org
On Feb 13, 2013, at 4:57 PM, Jan Kaliszewski <z...@chopin.edu.pl> wrote:

> and maybe also:
>
> class Color(Enum):
> # 0 1 2 3
> RED, GREEN, BLUE, YELLOW, *end = seq()
>
> class Color(Enum):
> # 3 4 5 6
> RED, GREEN, BLUE, YELLOW, *end = seq(3)
>
> class Flag(Enum):
> # 1 2 4 8 16
> FOO, BAR, BAZ, BOO, SPAM, *end = flags()
>
> (yes, it *is* possible to implement it without playing with stack frames...)

This suggestion interests me most. Would it require language changes for that *end stuff?

Ethan Furman

unread,
Feb 13, 2013, 11:50:36 PM2/13/13
to python...@python.org
On 02/13/2013 05:58 PM, Ryan Hiebert wrote:
> On Feb 13, 2013, at 4:57 PM, Jan Kaliszewski <z...@chopin.edu.pl> wrote:
>
>> and maybe also:
>>
>> class Color(Enum):
>> # 0 1 2 3
>> RED, GREEN, BLUE, YELLOW, *end = seq()
>>
>> class Color(Enum):
>> # 3 4 5 6
>> RED, GREEN, BLUE, YELLOW, *end = seq(3)
>>
>> class Flag(Enum):
>> # 1 2 4 8 16
>> FOO, BAR, BAZ, BOO, SPAM, *end = flags()
>>
>> (yes, it *is* possible to implement it without playing with stack frames...)
>
> This suggestion interests me most. Would it require language changes for that *end stuff?

Already is, since 3.0 I believe.

http://www.python.org/dev/peps/pep-3132/

--
~Ethan~

Ryan Hiebert

unread,
Feb 14, 2013, 12:34:53 AM2/14/13
to Ethan Furman, python...@python.org
On Feb 13, 2013, at 8:50 PM, Ethan Furman <et...@stoneleaf.us> wrote:

> On 02/13/2013 05:58 PM, Ryan Hiebert wrote:
>
>> On Feb 13, 2013, at 4:57 PM, Jan Kaliszewski <z...@chopin.edu.pl> wrote:
>>
>>> and maybe also:
>>>
>>> class Color(Enum):
>>> # 0 1 2 3
>>> RED, GREEN, BLUE, YELLOW, *end = seq()
>>>
>>> class Color(Enum):
>>> # 3 4 5 6
>>> RED, GREEN, BLUE, YELLOW, *end = seq(3)
>>>
>>> class Flag(Enum):
>>> # 1 2 4 8 16
>>> FOO, BAR, BAZ, BOO, SPAM, *end = flags()
>>>
>>> (yes, it *is* possible to implement it without playing with stack frames...)
>>
>> This suggestion interests me most. Would it require language changes for that *end stuff?
>
> Already is, since 3.0 I believe.
>
> http://www.python.org/dev/peps/pep-3132/

Wow, I've been missing out on a great feature. How is it able to avoid the stack frames? If I knew that, I'd love to write up an implementation.

Nick Coghlan

unread,
Feb 14, 2013, 1:18:12 AM2/14/13
to Steven D'Aprano, python...@python.org
On Thu, Feb 14, 2013 at 9:48 AM, Steven D'Aprano <st...@pearwood.info> wrote:
> Besides, the presence of a second, non-obvious solution is not a violation
> of One Obvious Way.

Something that is often forgotten is that having two ways to do
something is often *good* for your API design, because it lets you
design one simple API that covers a large fraction of use cases, and
then a more complex underlying API that covers all (or almost all) of
the rest.

A procedural wrapper around an object-oriented core is the classic
means of achieving this, and the standard library does it all over the
place (sometimes we don't publish the OO core, but the option is there
if our users demand the extra flexibility).

The trick to doing it well is to make sure that users are aware that
when the simple API is sufficient, that's the one they should use.
Only when that API is inadequate should they reach for the more
complex one. It's very, very easy to fall into the trap of documenting
the comprehensive core first, and then saying "oh, by the way, here's
this convenient helper function that means you will probably never
need to worry about all that underlying complexity".

The subprocess docs used to fall into that trap: call, check_call and
check_output cover many use cases, with Popen as the complex fallback,
but the old docs hit readers in the face with Popen, and only
mentioned the helper functions as an afterthought. The new docs have
the helper functions front and centre, with Popen relegated to "if you
really need it" status.

The flufl.enum docs *do* currently fall into the trap of treating
enum.make as an afterthought rather than as the preferred creation API
for the cases that it can handle, though.

Armin Ronacher has a lovely elaboration of this principle here:
http://lucumr.pocoo.org/2013/2/13/moar-classes/ (although he's mostly
complaining about the other direction, simple procedural APIs which
*don't* expose a flexible OO core, rather than the one here where an
OO core is exposed ).

Cheers,
Nick.

--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia

Antoine Pitrou

unread,
Feb 14, 2013, 2:35:58 AM2/14/13
to python...@python.org
On Thu, 14 Feb 2013 09:11:05 +1000
Nick Coghlan <ncog...@gmail.com> wrote:
> On 14 Feb 2013 02:39, "Antoine Pitrou" <soli...@pitrou.net> wrote:
> > >
> > > What's wrong with enum.make? That just accepts the sequence of names
> > > namedtuple style, no values specified anywhere.
> >
> > What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
> > different idioms for declaring enums, while a single class-based
> > definition style should suffice.
>
> Really? What happened to the principle of layered API complexity, where a
> simple *convenience* function is the obvious way to do it for the cases it
> covers, while the full, more powerful, but also more verbose, explicit
> subclassing API covers the more complex cases?

Yes, what happened to it?
Do you think enums are an important enough feature to warrant a
"layered API"? Why stop at 2 layers?

And why do you think subclassing is actually a complex API? It's
easily understood by any OO programmer out there, even non-Python
experts. Why do you think Python ORMs choose a subclassing API instead
of your so-called "obvious way to do it using a convenience function"?

Hint: nobody would find the convenience function API as obvious as the
subclassing API.

> Magic namespaces and metaclasses sure don't meet *my* definition of
> obvious.

Perhaps, but Python is not a supermarket where you choose your own brand
of obvious as you like. It's a programming language striving to
provide a rather consistent experience. People with bizarre tastes can
always publish their packages on PyPI.

Antoine Pitrou

unread,
Feb 14, 2013, 4:19:52 AM2/14/13
to python...@python.org
Le Wed, 13 Feb 2013 17:28:09 -0500,
Barry Warsaw <ba...@python.org> a écrit :

One of my common use cases for wanting an enum has always been to map a
third-party protocol or library's error codes.

Really, the one screaming use case in the stdlib is in the errno
module :-) For that you have to be able to define an enum that
subclasses int, though.

Nick Coghlan

unread,
Feb 14, 2013, 5:40:37 AM2/14/13
to Antoine Pitrou, python...@python.org
On Thu, Feb 14, 2013 at 5:35 PM, Antoine Pitrou <soli...@pitrou.net> wrote:
> Hint: nobody would find the convenience function API as obvious as the
> subclassing API.

# Status quo
one, two, three, four = range(4)

# flufl.enum.make -> types.new_enum
# Factory function for classes
# For when you don't care about the specific values
# Close parallel to collections.namedtuple
OneToFour = types.new_enum("OneToFour", "one two three four")

# flufl.enum.Enum -> types.Enum
# For when you do care about (some) specific values
class OneToFourAndTen(OneToFour):
ten = 10

collections.namedtuple is the normative example here, and while the
repetition of the class name is still annoying, that's a minor
irritation compared to completely violating everyone's expectations of
normal class behaviour.

>> Magic namespaces and metaclasses sure don't meet *my* definition of
>> obvious.
>
> Perhaps, but Python is not a supermarket where you choose your own brand
> of obvious as you like. It's a programming language striving to
> provide a rather consistent experience. People with bizarre tastes can
> always publish their packages on PyPI.

In which case, they should stop discussing them on python-ideas.
flufl.enum is a perfectly fine example of normal Pythonic class
design, with similar examples already in the standard library. The
approaches being thrown around on this list lately are very cool from
a technical point of view, and impressive illustrations of what
Python's metaclass machinery can do for you, but they're also
completely unintuitive black magic (including "x = ..." having any
result other than "x is Ellipsis").

Regards,
Nick.

--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia

Antoine Pitrou

unread,
Feb 14, 2013, 6:16:02 AM2/14/13
to python...@python.org
Le Thu, 14 Feb 2013 20:40:37 +1000,

Nick Coghlan <ncog...@gmail.com> a
écrit :
>
> collections.namedtuple is the normative example here, and while the
> repetition of the class name is still annoying, that's a minor
> irritation compared to completely violating everyone's expectations of
> normal class behaviour.

I find it slightly amusing that you're complaining about a violation
one of your latest PEPs is trying to make easier to make.

But, still, you are not convincing me that namedtuple is normative in
any way. The fact that many people would prefer a class-based
declarative syntax for namedtuple is proof.

By the way, not only is the repetition of the class name annoying, but
it also makes subclassing more annoying too. Most of my uses of
namedtuple imply subclassing, because I add some behaviour (for
example additional constructors or serializers).

So, compare:

_BaseProtocolItem = namedtuple('_BaseProtocolItem',
('serial_no', 'command'))

class ProtocolItem(_BaseProtocolItem):
# snip custom methods


To the hypothetical:

class ProtocolItem(NamedTuple):
__fields__ = ('serial_no', 'command')

# snip custom methods


And you understand why the namedtuple() factory function is really an
inferior solution with too much typing.

> In which case, they should stop discussing them on python-ideas.
> flufl.enum is a perfectly fine example of normal Pythonic class
> design, with similar examples already in the standard library. The
> approaches being thrown around on this list lately are very cool from
> a technical point of view, and impressive illustrations of what
> Python's metaclass machinery can do for you, but they're also
> completely unintuitive black magic (including "x = ..." having any
> result other than "x is Ellipsis").

You're still ignoring the less-magic solutions such as:

class Errno(Enum):
__enumvalues__ = ('EBADF', 'ENOENT',)


Regards

Antoine.

Richard Oudkerk

unread,
Feb 14, 2013, 6:34:23 AM2/14/13
to python...@python.org
On 14/02/2013 12:57am, Jan Kaliszewski wrote:
> and maybe also:
>
> class Color(Enum):
> # 0 1 2 3
> RED, GREEN, BLUE, YELLOW, *end = seq()
>
> class Color(Enum):
> # 3 4 5 6
> RED, GREEN, BLUE, YELLOW, *end = seq(3)
>
> class Flag(Enum):
> # 1 2 4 8 16
> FOO, BAR, BAZ, BOO, SPAM, *end = flags()
>
> (yes, it *is* possible to implement it without playing with stack
> frames...)

Are seq and flags infinite generators/iterators? If so then this won't
work because end will be a *list* containing the contents of the tail of
the iterator.

>>> def gen():
... i = 0
... while True:
... yield i
... i += 1
...
>>> a, b, *end = gen()
<Hangs, consuming more and more memory>

--
Richard

Nick Coghlan

unread,
Feb 14, 2013, 8:02:44 AM2/14/13
to Antoine Pitrou, python...@python.org
On Thu, Feb 14, 2013 at 9:16 PM, Antoine Pitrou <soli...@pitrou.net> wrote:

> You're still ignoring the less-magic solutions such as:
>
> class Errno(Enum):
> __enumvalues__ = ('EBADF', 'ENOENT',)

Yes, an approach like that, or anything similarly explicit would be
fine with me (and has the virtue of potentially supporting pickle,
unlike anonymous class generators like the current namedtuple
implementation). It was the clever insanity of some of the other ideas
being kicked around in these threads that I wanted to be clear I would
fight tooth and nail if they were ever actually proposed for stdlib
inclusion.

My enthusiasm for flufl.enum in particular is mainly of the "I'm tired
of seeing almost the exact same enum discussion get rehashed every
couple of years, and that one looks good enough to me" variety, so it
probably isn't wise to pay too much attention to me when it comes to
the specific of the API :P

Your comments did give me an idea about namedtuple though - I'll start
another thread about that.

> I find it slightly amusing that you're complaining about a violation
> one of your latest PEPs is trying to make easier to make.

[Replying to this second, since it is getting into philosophy of
language design, and I actually agree with your key point above]

I have a long history of trying to demystify various aspects of Python
metaprogramming and make it easier for people to explore the language,
and create suitable meta-frameworks for their own use, and for use
within particular subcommunities.

As I see it, violating normal expectations for class behaviour *within
the context of a framework* is fine, *so long as you can't achieve
reasonable syntax in a simpler way*. Environments like Django, SQL
Alchemy, etc set up new norms about expected behaviour in different
kinds of classes, and that's the entire reason I think these
metaprograming tools are worth providing in the first place (although
I acknowledge it *can* cause problems when people learn one of these
frameworks *first*, and then later need to be taught the difference
between the automatically provided framework level behaviour for
Forms, Models, Views, etc and the underlying standard language
behaviour).

While the standard library already provides three distinct class level
behaviours (normal classes, subclasses of type and ABCs), those are
each intertwined deeply with the core type system (arguably,
inheriting from builtins other than type or object counts as an
additional case, since inheriting from a C defined class that isn't
really designed for it has interesting consequences in CPython due to
implementation limitations). Furthermore, while our variants change
the way instantiation, descriptors, isinstance and issubclass behave
after the class is created, they *don't* alter the runtime semantics
of the class body itself.

To my mind, defining enumerations doesn't even come close to reaching
the bar that would justify highly customised class behaviour that a
reader can't guess simply from knowing Python and reading the code (I
get the impression you already agree with me on this point, which is
why I moved this philosophical section after my agreement with you
above).

Cheers,
Nick

--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia

João Bernardo

unread,
Feb 14, 2013, 8:54:43 PM2/14/13
to Nick Coghlan, Antoine Pitrou, Python-Ideas
And now for something completely different:

If the Enums are used with attribute syntax, why not this?
>>> Enum.Colors.red.green.blue
<Enum: Colors>
 
>>> Enum.Colors.red
<Colors.red = 0>
>>> Enum.Colors.green
<Colors.green = 1>
>>> Enum.Colors.blue
<Colors.blue = 2>
This is my 5-minute implementation (quite fun)

Looks like everybody is fighting against the class syntax and it is very incompatible with the notion of enum we've got from other languages.
BTW, I'm a big fan of flufl.enum


João Bernardo


2013/2/14 Nick Coghlan <ncog...@gmail.com>
It is loading more messages.
0 new messages