PEP 435 - requesting pronouncement

Hello pydev, PEP 435 is ready for final review. A lot of the feedback from the last few weeks of discussions has been incorporated. Naturally, not everything could go in because some minor (mostly preference-based) issues did not reach a consensus. We do feel, however, that the end result is better than in the beginning and that Python can finally have a useful enumeration type in the standard library. I'm attaching the latest version of the PEP for convenience. If you've read previous versions, the easiest way to get acquainted with the recent changes is to go through the revision log at http://hg.python.org/peps A reference implementation for PEP 435 is available at https://bitbucket.org/stoneleaf/ref435 Kind regards and happy weekend.

Typo line 171: <Colro.blue: 3> One thing I'd like to be clear in the PEP about is whether enum_type and _EnumDict._enum_names should be documented, or whether they're considered implementation details. I'd like to make a subclass of Enum that accepts ... for auto-valued enums but that requires subclassing the metaclass and access to classdict._enum_names. I can get to enum_type via type(Enum), but _EnumDict._enum_names requires knowing the attribute. It would sufficient for my purposes if it was just documented that the passed classdict had a _enum_names attribute. In testing the below, I've also discovered a bug in the reference implementation - currently it will not handle an __mro__ like: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>, <class 'int'>, <Enum 'Enum'>, <class 'object'>) Apply the following patch to make that work: diff -r 758d43b9f732 ref435.py --- a/ref435.py Fri May 03 18:59:32 2013 -0700 +++ b/ref435.py Sun May 05 09:23:25 2013 +1000 @@ -116,7 +116,11 @@ if bases[-1] is Enum: obj_type = bases[0] else: - obj_type = bases[-1].__mro__[1] # e.g. (IntEnum, int, Enum, object) + for base in bases[-1].__mro__: + if not issubclass(base, Enum): + obj_type = base + break + else: obj_type = object # save enum items into separate mapping so they don't get baked into My auto-enum implementation (using the above patch - without it you can get the essentially the same results with class AutoIntEnum(int, Enum, metaclass=auto_enum). class auto_enum(type(Enum)): def __new__(metacls, cls, bases, classdict): temp = type(classdict)() names = set(classdict._enum_names) i = 0 for k in classdict._enum_names: v = classdict[k] if v is Ellipsis: v = i else: i = v i += 1 temp[k] = v for k, v in classdict.items(): if k not in names: temp[k] = v return super(auto_enum, metacls).__new__(metacls, cls, bases, temp) class AutoNumberedEnum(Enum, metaclass=auto_enum): pass class AutoIntEnum(IntEnum, metaclass=auto_enum): pass class TestAutoNumber(AutoNumberedEnum): a = ... b = 3 c = ... class TestAutoInt(AutoIntEnum): a = ... b = 3 c = ... print(TestAutoNumber, list(TestAutoNumber)) print(TestAutoInt, list(TestAutoInt)) ---------- Run ---------- <Enum 'TestAutoNumber'> [<TestAutoNumber.a: 0>, <TestAutoNumber.b: 3>, <TestAutoNumber.c: 4>] <Enum 'TestAutoInt'> [<TestAutoInt.a: 0>, <TestAutoInt.b: 3>, <TestAutoInt.c: 4>] Tim Delaney

On Sat, May 4, 2013 at 4:27 PM, Tim Delaney <timothy.c.delaney@gmail.com>wrote:
Typo line 171: <Colro.blue: 3>
Fixed, thanks.
No, they should not. Not only are they implementation details, they are details of the *reference implementation*, not the actual stdlib module. The reference implementation will naturally serve as a basis for the stdlib module, but it still has to undergo a review in which implementation details can change. Note that usually we do not document implementation details of stdlib modules, but this doesn't prevent some people from using them if they really want to.
Thanks! Tim - did you sign the contributor CLA for Python? Since the reference implementation is aimed for becoming the stdlib enum eventually, we'd probably need you to sign that before we can accept patches from you. Eli

On 5 May 2013 10:49, Eli Bendersky <eliben@gmail.com> wrote:
I think it would be useful to have some guaranteed method for a sub-metaclass to get the list of enum keys before calling the base class __new__. Not being able to do so removes a large number of possible extensions (like auto-numbering).
I have now (just waiting on the confirmation email). Haven't submitted a patch since the CLAs were started ... Tim Delaney

On 5 May 2013 11:22, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
I've been able to achieve the auto-numbering without relying on the internal implementation at all (with a limitation), with a single change to enum_type.__new__. My previous patch was slightly wrong - fix below as well. All existing tests pass. BTW, for mix-ins it's required that they have __slots__ = () - might want to mention that in the PEP. diff -r 758d43b9f732 ref435.py --- a/ref435.py Fri May 03 18:59:32 2013 -0700 +++ b/ref435.py Sun May 05 13:10:11 2013 +1000 @@ -116,7 +116,17 @@ if bases[-1] is Enum: obj_type = bases[0] else: - obj_type = bases[-1].__mro__[1] # e.g. (IntEnum, int, Enum, object) + obj_type = None + + for base in bases: + for c in base.__mro__: + if not issubclass(c, Enum): + obj_type = c + break + + if obj_type is not None: + break + else: obj_type = object # save enum items into separate mapping so they don't get baked into @@ -142,6 +152,7 @@ if obj_type in (object, Enum): enum_item = object.__new__(enum_class) else: + value = obj_type.__new__(obj_type, value) enum_item = obj_type.__new__(enum_class, value) enum_item._value = value enum_item._name = e Implementation: class AutoInt(int): __slots__ = () # Required def __new__(cls, value): if value is Ellipsis: try: i = cls._auto_number except AttributeError: i = cls._auto_number = 0 else: i = cls._auto_number = value cls._auto_number += 1 return int.__new__(cls, i) class AutoIntEnum(AutoInt, IntEnum): pass class TestAutoIntEnum(AutoIntEnum): a = ... b = 3 c = ... print(TestAutoIntEnum, list(TestAutoIntEnum)) ---------- Run ---------- <Enum 'TestAutoIntEnum'> [<TestAutoIntEnum.a: 0>, <TestAutoIntEnum.b: 3>, <TestAutoIntEnum.c: 4>] The implementation is not quite as useful - there's no immediately-obvious way to have an auto-numbered enum that is not also an int enum e.g. if you define class AutoNumberedEnum(AutoInt, Enum) it's still an int subclass. Tim Delaney

On 5 May 2013 13:32, Ethan Furman <ethan@stoneleaf.us> wrote:
Traceback (most recent call last): File "D:\Development\ref435\ref435.py", line 311, in <module> class AutoIntEnum(AutoInt, IntEnum): File "D:\Development\ref435\ref435.py", line 138, in __new__ enum_class = type.__new__(metacls, cls, bases, classdict) TypeError: multiple bases have instance lay-out conflict Tim Delaney

On Sat, 4 May 2013 15:04:49 -0700 Eli Bendersky <eliben@gmail.com> wrote:
I still would like to see Nick's class-based API preferred over the functional API: class Season(Enum, members='spring summer autumn'): pass The PEP doesn't even mention it, even though you got significant pushback on the proposed _getframe() hack for pickling (including mentions that IronPython and Cython may not support it), and nobody seemed to be unhappy with the class-based proposal. Regards Antoine.

On 05/05/13 20:05, Antoine Pitrou wrote:
-1 As already mentioned, this is no substitute for the functional API as it is a statement, not an expression. As for pickling, the usual restrictions on pickling apply. It's not like the functional API creates new and unexpected restrictions. -- Steven

On Sun, 05 May 2013 20:59:03 +1000 Steven D'Aprano <steve@pearwood.info> wrote:
So, can you explain why it would make a difference?
As for pickling, the usual restrictions on pickling apply.
No. I'm sure pickling classes normally works on Cython and IronPython, and with PEP 3154 pickling nested classes will also be supported. Regards Antoine.

On 05/05/2013 03:05 AM, Antoine Pitrou wrote:
Agreed that the PEP should mention it. -1 on using it. We don't need two different ways to use class syntax. The functional interface is there for two reasons: - to easily create enums dynamically (fairly rare, I'm sure) - to easily create enums when prototyping or at the interactive prompt (I'll use it all the time -- it's convenient! ;) -- ~Ethan~

On May 5, 2013, at 6:44 AM, Ethan Furman wrote:
+1
I don't understand, the class based API is perfectly fine for prototyping in the repl. For dynamic creation, the class API always provides a functional API for free: import types types.new_class('Season', (Enum,), dict(values='spring summer autumn')) It's not convenient, but that doesn't matter because this usage is rare anyway. Certainly much rarer than declarations of auto-numbered enums. -- Philip Jenvey

On May 05, 2013, at 06:44 AM, Ethan Furman wrote:
to easily create enums when prototyping or at the interactive prompt (I'll use it all the time -- it's convenient! ;)
+1billion (That's literally the number of times I've used the functional API when discussion various aspects of enum behavior :). -Barry

On Sun, May 5, 2013 at 3:05 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Plenty of points were raised against having this members= API. People argued ardently both ways .Guido publicly asked to decide in favor of the functional API, and we added an explicit warning about pickling (which was lifted from the docs of pickle itself). If you feel this has to be discussed further, please open a new thread. I don't want another 100 bikeshedding emails to go into this one. We'll mention this as a considered alternative in the PEP, though.
and nobody seemed to be unhappy with the class-based proposal.
Not true, as you see. Eli

On Sun, 5 May 2013 07:09:14 -0700 Eli Bendersky <eliben@gmail.com> wrote:
The main point seems to be "I don't like it". If you consider this a strong argument against the concrete issues with the functional API, then good for you.
This is not true. The pickling restrictions which have been raised are specifically caused by the functional syntax, something which your warning omits.
This is not bikeshedding since it addresses concrete functional issues. (but apparently you would very much like to sweep those issues under the carpet in the name of "bikeshedding") Regards Antoine.

I am fine with adding more information about this issue to the PEP. I am not fine with reopening the issue. I really, really, really have looked at it from all sides and the current design of the functional API has my full blessing. On Sun, May 5, 2013 at 10:46 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
-- --Guido van Rossum (python.org/~guido)

On Sun, May 5, 2013 at 10:46 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
I'm sorry that you're taking this issue so personally, Antoine. As for pickling enums created with the functional API, I don't think we now provide less than the pickle module dictates in the general sense. The pickle docs say: The following types can be pickled: [...] - classes that are defined at the top level of a module - instances of such classes whose __dict__ or the result of calling __getstate__() is picklable (see section *Pickling Class Instances*<http://docs.python.org/dev/library/pickle.html?highlight=pickle#pickle-inst>for details). I'll open a separate thread about how this can be implemented and documented in the best way possible, but I really don't see it as an unsolvable issue. Eli

On Sun, May 5, 2013 at 3:05 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
This particular bikeshed has sailed. I heard all the feedback, took into account my own thoughts, and have decided that we should go ahead with this syntax and the _getframe() hack. If the _getframe() hack doesn't work on a given platform, the __module__ attribute is not set correctly, so pickling will fail, but everything else will work. We can work on a PEP to replace the _getframe() hack separately; I think it's functionality that is useful beyond Enum() and namedtuple(), and can be implemented on all platforms with something a lot less general than _getframe(). Authors can also avoid the _getframe() hack in two ways: (a) use the full class definition; (b) specify the full dotted class name in the call. (We should modify namedtuple() to support this too BTW.) -- --Guido van Rossum (python.org/~guido)

I'm chiming in late, but am I the only one who's really bothered by the syntax? class Color(Enum): red = 1 green = 2 blue = 3 I really don't see why one has to provide values, since an enum constant *is* the value. In many cases, there's no natural mapping between an enum constant and a value, e.g. there's no reason why Color.red should be mapped to 1 and Color.blue to 3. Furthermore, the PEP makes it to possible to do something like: class Color(Enum): red = 1 green = 2 blue = 3 red_alias = 1 which is IMO really confusing, since enum instances are supposed to be distinct. All the languages I can think of that support explicit values (Java being particular in the sense that it's really a full-fledge object which can have attributes, methods, etc) make it optional by default. Finally, I think 99% of users won't care about the assigned value (which is just an implementation detail), so explicit value will be just noise annoying users (well, me at least :-). cf 2013/5/5 Eli Bendersky <eliben@gmail.com>:

This has all long been hashed out, and I've pronounced on this already. I'm sorry you weren't there for the bikeshedding, but nothing you say here is new and it was all considered carefully. On Sun, May 5, 2013 at 10:07 AM, Charles-François Natali <cf.natali@gmail.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On 06/05/13 03:07, Charles-François Natali wrote:
The functional API provides a way to conveniently create enums without caring what value they get. Other than that, the PEP explains that there was an early proposal to declare names without values: # rejected syntax class Color(Enum): red green blue but this was rejected for being too magical, and too confusing to those who aren't expecting it.
Enums often have duplicate values, sometimes to provide aliases, sometimes to correct spelling errors, or to manage deprecated names, etc. class Color(Enum): red = 1 green = 2 # this is the preferred spelling blue = 3 gren = green # oops, do not remove, needed for backwards compatibility E.g. I googled on "C enum" and the very first hit includes a duplicate value: http://msdn.microsoft.com/en-AU/library/whbyts4t%28v=vs.80%29.aspx And two examples from asm-generic/errno.h: #define EWOULDBLOCK EAGAIN /* Operation would block */ #define EDEADLOCK EDEADLK
Compatibility with (e.g.) C enums is an important use-case for these, and in that case you likely will care about the actual value. -- Steven

OK, I thought I'd take a look. I have never particularly needed enums in real life, so I'm reading the PEP from the POV of a naive user who is just thinking "hey, neat, Python got enums, let's see how they work". I have been skimming the discussions and my head has been exploding with the complexity, so I admit I was very, very scared that the PEP might be equally daunting. First, the good news - from the POV described above, the PEP is both readable and intuitive. Nice job, guys! Now the problems I had: 1. Having to enter the values is annoying. Sorry, I read the rationale and all that, and I *still* want to write a C-Like enum { A, B, C }. I fully expect to edit and reorder enums (if I ever use them) and get irritated with having to update the value assignments. 2. Enums are not orderable by default. Yuk. I doubt I'll care about this often (iteration is more important) but when I do, I'll be annoyed. 3. This is just a thought, but I suspect that IntEnums iterating in definition order but ordering by value could trip people up and cause hard to diagnose bugs. 4. I'll either use the functional form all the time (because I don't have to specify values) or never (because it's ugly as sin). I can't work out which aspect will win yet. And one omission that struck me. There's no mention of the common case of bitmap enums. class Example(Enum): a = 1 b = 2 c = 4 Do I need to use an IntEnum (given the various warnings in the PEP about how "most people won't need it") if I want to be able to do things like flags = "Example.a | Example.c"? I think there should at least be an extended example in the PEP covering a bitmap enum case. (And certainly the final documentation should include a cookbook-style example of bitmap enums). Summary - good job, I like the PEP a lot. But Python's enums are very unlike those of other languages, and I suspect that's going to be more of an issue than you'd hope... Paul. On 4 May 2013 23:04, Eli Bendersky <eliben@gmail.com> wrote:

On Sun, May 5, 2013 at 10:41 AM, Paul Moore <p.f.moore@gmail.com> wrote:
I guess there are cultural differences around this. Anyway, you can use the functional/convenience API for this purpose.
2. Enums are not orderable by default. Yuk. I doubt I'll care about this often (iteration is more important) but when I do, I'll be annoyed.
I personally agree with you, but not strongly enough to override Barry and Eli who seem to be strongly for unordered enums.
This is somewhat in conflict with your #1. :-)
But not everybody will make the same choice.
You'd have to use IntEnum. Plus, these are hardly enums -- they are a particularly obscure old school hack for representing sets of flags. (I liked Pascal's solution for this better -- it had a bit set data structure that supported sets of enums.)
We're pretty confident that we're doing about the best job possible given the constraints (one of which is getting this accepted into Python 3.4 without any of the participants incurring permanent brain damage). -- --Guido van Rossum (python.org/~guido)

Guido van Rossum <guido@python.org> writes:
Would it be wise to forbid ... as an enum value to preserve the option to use it for automatic value assignment in some indefinite future? Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On Sun, May 5, 2013 at 4:15 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
Correct. *If* we were to have a change of heart on this issue, we'd just introduce a class AutoNumberEnum. But I find the "..." syntax sufficiently ugly that I really don't expect I'll ever change my mind. -- --Guido van Rossum (python.org/~guido)

Typo line 171: <Colro.blue: 3> One thing I'd like to be clear in the PEP about is whether enum_type and _EnumDict._enum_names should be documented, or whether they're considered implementation details. I'd like to make a subclass of Enum that accepts ... for auto-valued enums but that requires subclassing the metaclass and access to classdict._enum_names. I can get to enum_type via type(Enum), but _EnumDict._enum_names requires knowing the attribute. It would sufficient for my purposes if it was just documented that the passed classdict had a _enum_names attribute. In testing the below, I've also discovered a bug in the reference implementation - currently it will not handle an __mro__ like: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>, <class 'int'>, <Enum 'Enum'>, <class 'object'>) Apply the following patch to make that work: diff -r 758d43b9f732 ref435.py --- a/ref435.py Fri May 03 18:59:32 2013 -0700 +++ b/ref435.py Sun May 05 09:23:25 2013 +1000 @@ -116,7 +116,11 @@ if bases[-1] is Enum: obj_type = bases[0] else: - obj_type = bases[-1].__mro__[1] # e.g. (IntEnum, int, Enum, object) + for base in bases[-1].__mro__: + if not issubclass(base, Enum): + obj_type = base + break + else: obj_type = object # save enum items into separate mapping so they don't get baked into My auto-enum implementation (using the above patch - without it you can get the essentially the same results with class AutoIntEnum(int, Enum, metaclass=auto_enum). class auto_enum(type(Enum)): def __new__(metacls, cls, bases, classdict): temp = type(classdict)() names = set(classdict._enum_names) i = 0 for k in classdict._enum_names: v = classdict[k] if v is Ellipsis: v = i else: i = v i += 1 temp[k] = v for k, v in classdict.items(): if k not in names: temp[k] = v return super(auto_enum, metacls).__new__(metacls, cls, bases, temp) class AutoNumberedEnum(Enum, metaclass=auto_enum): pass class AutoIntEnum(IntEnum, metaclass=auto_enum): pass class TestAutoNumber(AutoNumberedEnum): a = ... b = 3 c = ... class TestAutoInt(AutoIntEnum): a = ... b = 3 c = ... print(TestAutoNumber, list(TestAutoNumber)) print(TestAutoInt, list(TestAutoInt)) ---------- Run ---------- <Enum 'TestAutoNumber'> [<TestAutoNumber.a: 0>, <TestAutoNumber.b: 3>, <TestAutoNumber.c: 4>] <Enum 'TestAutoInt'> [<TestAutoInt.a: 0>, <TestAutoInt.b: 3>, <TestAutoInt.c: 4>] Tim Delaney

On Sat, May 4, 2013 at 4:27 PM, Tim Delaney <timothy.c.delaney@gmail.com>wrote:
Typo line 171: <Colro.blue: 3>
Fixed, thanks.
No, they should not. Not only are they implementation details, they are details of the *reference implementation*, not the actual stdlib module. The reference implementation will naturally serve as a basis for the stdlib module, but it still has to undergo a review in which implementation details can change. Note that usually we do not document implementation details of stdlib modules, but this doesn't prevent some people from using them if they really want to.
Thanks! Tim - did you sign the contributor CLA for Python? Since the reference implementation is aimed for becoming the stdlib enum eventually, we'd probably need you to sign that before we can accept patches from you. Eli

On 5 May 2013 10:49, Eli Bendersky <eliben@gmail.com> wrote:
I think it would be useful to have some guaranteed method for a sub-metaclass to get the list of enum keys before calling the base class __new__. Not being able to do so removes a large number of possible extensions (like auto-numbering).
I have now (just waiting on the confirmation email). Haven't submitted a patch since the CLAs were started ... Tim Delaney

On 5 May 2013 11:22, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
I've been able to achieve the auto-numbering without relying on the internal implementation at all (with a limitation), with a single change to enum_type.__new__. My previous patch was slightly wrong - fix below as well. All existing tests pass. BTW, for mix-ins it's required that they have __slots__ = () - might want to mention that in the PEP. diff -r 758d43b9f732 ref435.py --- a/ref435.py Fri May 03 18:59:32 2013 -0700 +++ b/ref435.py Sun May 05 13:10:11 2013 +1000 @@ -116,7 +116,17 @@ if bases[-1] is Enum: obj_type = bases[0] else: - obj_type = bases[-1].__mro__[1] # e.g. (IntEnum, int, Enum, object) + obj_type = None + + for base in bases: + for c in base.__mro__: + if not issubclass(c, Enum): + obj_type = c + break + + if obj_type is not None: + break + else: obj_type = object # save enum items into separate mapping so they don't get baked into @@ -142,6 +152,7 @@ if obj_type in (object, Enum): enum_item = object.__new__(enum_class) else: + value = obj_type.__new__(obj_type, value) enum_item = obj_type.__new__(enum_class, value) enum_item._value = value enum_item._name = e Implementation: class AutoInt(int): __slots__ = () # Required def __new__(cls, value): if value is Ellipsis: try: i = cls._auto_number except AttributeError: i = cls._auto_number = 0 else: i = cls._auto_number = value cls._auto_number += 1 return int.__new__(cls, i) class AutoIntEnum(AutoInt, IntEnum): pass class TestAutoIntEnum(AutoIntEnum): a = ... b = 3 c = ... print(TestAutoIntEnum, list(TestAutoIntEnum)) ---------- Run ---------- <Enum 'TestAutoIntEnum'> [<TestAutoIntEnum.a: 0>, <TestAutoIntEnum.b: 3>, <TestAutoIntEnum.c: 4>] The implementation is not quite as useful - there's no immediately-obvious way to have an auto-numbered enum that is not also an int enum e.g. if you define class AutoNumberedEnum(AutoInt, Enum) it's still an int subclass. Tim Delaney

On 5 May 2013 13:32, Ethan Furman <ethan@stoneleaf.us> wrote:
Traceback (most recent call last): File "D:\Development\ref435\ref435.py", line 311, in <module> class AutoIntEnum(AutoInt, IntEnum): File "D:\Development\ref435\ref435.py", line 138, in __new__ enum_class = type.__new__(metacls, cls, bases, classdict) TypeError: multiple bases have instance lay-out conflict Tim Delaney

On Sat, 4 May 2013 15:04:49 -0700 Eli Bendersky <eliben@gmail.com> wrote:
I still would like to see Nick's class-based API preferred over the functional API: class Season(Enum, members='spring summer autumn'): pass The PEP doesn't even mention it, even though you got significant pushback on the proposed _getframe() hack for pickling (including mentions that IronPython and Cython may not support it), and nobody seemed to be unhappy with the class-based proposal. Regards Antoine.

On 05/05/13 20:05, Antoine Pitrou wrote:
-1 As already mentioned, this is no substitute for the functional API as it is a statement, not an expression. As for pickling, the usual restrictions on pickling apply. It's not like the functional API creates new and unexpected restrictions. -- Steven

On Sun, 05 May 2013 20:59:03 +1000 Steven D'Aprano <steve@pearwood.info> wrote:
So, can you explain why it would make a difference?
As for pickling, the usual restrictions on pickling apply.
No. I'm sure pickling classes normally works on Cython and IronPython, and with PEP 3154 pickling nested classes will also be supported. Regards Antoine.

On 05/05/2013 03:05 AM, Antoine Pitrou wrote:
Agreed that the PEP should mention it. -1 on using it. We don't need two different ways to use class syntax. The functional interface is there for two reasons: - to easily create enums dynamically (fairly rare, I'm sure) - to easily create enums when prototyping or at the interactive prompt (I'll use it all the time -- it's convenient! ;) -- ~Ethan~

On May 5, 2013, at 6:44 AM, Ethan Furman wrote:
+1
I don't understand, the class based API is perfectly fine for prototyping in the repl. For dynamic creation, the class API always provides a functional API for free: import types types.new_class('Season', (Enum,), dict(values='spring summer autumn')) It's not convenient, but that doesn't matter because this usage is rare anyway. Certainly much rarer than declarations of auto-numbered enums. -- Philip Jenvey

On May 05, 2013, at 06:44 AM, Ethan Furman wrote:
to easily create enums when prototyping or at the interactive prompt (I'll use it all the time -- it's convenient! ;)
+1billion (That's literally the number of times I've used the functional API when discussion various aspects of enum behavior :). -Barry

On Sun, May 5, 2013 at 3:05 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Plenty of points were raised against having this members= API. People argued ardently both ways .Guido publicly asked to decide in favor of the functional API, and we added an explicit warning about pickling (which was lifted from the docs of pickle itself). If you feel this has to be discussed further, please open a new thread. I don't want another 100 bikeshedding emails to go into this one. We'll mention this as a considered alternative in the PEP, though.
and nobody seemed to be unhappy with the class-based proposal.
Not true, as you see. Eli

On Sun, 5 May 2013 07:09:14 -0700 Eli Bendersky <eliben@gmail.com> wrote:
The main point seems to be "I don't like it". If you consider this a strong argument against the concrete issues with the functional API, then good for you.
This is not true. The pickling restrictions which have been raised are specifically caused by the functional syntax, something which your warning omits.
This is not bikeshedding since it addresses concrete functional issues. (but apparently you would very much like to sweep those issues under the carpet in the name of "bikeshedding") Regards Antoine.

I am fine with adding more information about this issue to the PEP. I am not fine with reopening the issue. I really, really, really have looked at it from all sides and the current design of the functional API has my full blessing. On Sun, May 5, 2013 at 10:46 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
-- --Guido van Rossum (python.org/~guido)

On Sun, May 5, 2013 at 10:46 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
I'm sorry that you're taking this issue so personally, Antoine. As for pickling enums created with the functional API, I don't think we now provide less than the pickle module dictates in the general sense. The pickle docs say: The following types can be pickled: [...] - classes that are defined at the top level of a module - instances of such classes whose __dict__ or the result of calling __getstate__() is picklable (see section *Pickling Class Instances*<http://docs.python.org/dev/library/pickle.html?highlight=pickle#pickle-inst>for details). I'll open a separate thread about how this can be implemented and documented in the best way possible, but I really don't see it as an unsolvable issue. Eli

On Sun, May 5, 2013 at 3:05 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
This particular bikeshed has sailed. I heard all the feedback, took into account my own thoughts, and have decided that we should go ahead with this syntax and the _getframe() hack. If the _getframe() hack doesn't work on a given platform, the __module__ attribute is not set correctly, so pickling will fail, but everything else will work. We can work on a PEP to replace the _getframe() hack separately; I think it's functionality that is useful beyond Enum() and namedtuple(), and can be implemented on all platforms with something a lot less general than _getframe(). Authors can also avoid the _getframe() hack in two ways: (a) use the full class definition; (b) specify the full dotted class name in the call. (We should modify namedtuple() to support this too BTW.) -- --Guido van Rossum (python.org/~guido)

I'm chiming in late, but am I the only one who's really bothered by the syntax? class Color(Enum): red = 1 green = 2 blue = 3 I really don't see why one has to provide values, since an enum constant *is* the value. In many cases, there's no natural mapping between an enum constant and a value, e.g. there's no reason why Color.red should be mapped to 1 and Color.blue to 3. Furthermore, the PEP makes it to possible to do something like: class Color(Enum): red = 1 green = 2 blue = 3 red_alias = 1 which is IMO really confusing, since enum instances are supposed to be distinct. All the languages I can think of that support explicit values (Java being particular in the sense that it's really a full-fledge object which can have attributes, methods, etc) make it optional by default. Finally, I think 99% of users won't care about the assigned value (which is just an implementation detail), so explicit value will be just noise annoying users (well, me at least :-). cf 2013/5/5 Eli Bendersky <eliben@gmail.com>:

This has all long been hashed out, and I've pronounced on this already. I'm sorry you weren't there for the bikeshedding, but nothing you say here is new and it was all considered carefully. On Sun, May 5, 2013 at 10:07 AM, Charles-François Natali <cf.natali@gmail.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On 06/05/13 03:07, Charles-François Natali wrote:
The functional API provides a way to conveniently create enums without caring what value they get. Other than that, the PEP explains that there was an early proposal to declare names without values: # rejected syntax class Color(Enum): red green blue but this was rejected for being too magical, and too confusing to those who aren't expecting it.
Enums often have duplicate values, sometimes to provide aliases, sometimes to correct spelling errors, or to manage deprecated names, etc. class Color(Enum): red = 1 green = 2 # this is the preferred spelling blue = 3 gren = green # oops, do not remove, needed for backwards compatibility E.g. I googled on "C enum" and the very first hit includes a duplicate value: http://msdn.microsoft.com/en-AU/library/whbyts4t%28v=vs.80%29.aspx And two examples from asm-generic/errno.h: #define EWOULDBLOCK EAGAIN /* Operation would block */ #define EDEADLOCK EDEADLK
Compatibility with (e.g.) C enums is an important use-case for these, and in that case you likely will care about the actual value. -- Steven

OK, I thought I'd take a look. I have never particularly needed enums in real life, so I'm reading the PEP from the POV of a naive user who is just thinking "hey, neat, Python got enums, let's see how they work". I have been skimming the discussions and my head has been exploding with the complexity, so I admit I was very, very scared that the PEP might be equally daunting. First, the good news - from the POV described above, the PEP is both readable and intuitive. Nice job, guys! Now the problems I had: 1. Having to enter the values is annoying. Sorry, I read the rationale and all that, and I *still* want to write a C-Like enum { A, B, C }. I fully expect to edit and reorder enums (if I ever use them) and get irritated with having to update the value assignments. 2. Enums are not orderable by default. Yuk. I doubt I'll care about this often (iteration is more important) but when I do, I'll be annoyed. 3. This is just a thought, but I suspect that IntEnums iterating in definition order but ordering by value could trip people up and cause hard to diagnose bugs. 4. I'll either use the functional form all the time (because I don't have to specify values) or never (because it's ugly as sin). I can't work out which aspect will win yet. And one omission that struck me. There's no mention of the common case of bitmap enums. class Example(Enum): a = 1 b = 2 c = 4 Do I need to use an IntEnum (given the various warnings in the PEP about how "most people won't need it") if I want to be able to do things like flags = "Example.a | Example.c"? I think there should at least be an extended example in the PEP covering a bitmap enum case. (And certainly the final documentation should include a cookbook-style example of bitmap enums). Summary - good job, I like the PEP a lot. But Python's enums are very unlike those of other languages, and I suspect that's going to be more of an issue than you'd hope... Paul. On 4 May 2013 23:04, Eli Bendersky <eliben@gmail.com> wrote:

On Sun, May 5, 2013 at 10:41 AM, Paul Moore <p.f.moore@gmail.com> wrote:
I guess there are cultural differences around this. Anyway, you can use the functional/convenience API for this purpose.
2. Enums are not orderable by default. Yuk. I doubt I'll care about this often (iteration is more important) but when I do, I'll be annoyed.
I personally agree with you, but not strongly enough to override Barry and Eli who seem to be strongly for unordered enums.
This is somewhat in conflict with your #1. :-)
But not everybody will make the same choice.
You'd have to use IntEnum. Plus, these are hardly enums -- they are a particularly obscure old school hack for representing sets of flags. (I liked Pascal's solution for this better -- it had a bit set data structure that supported sets of enums.)
We're pretty confident that we're doing about the best job possible given the constraints (one of which is getting this accepted into Python 3.4 without any of the participants incurring permanent brain damage). -- --Guido van Rossum (python.org/~guido)

Guido van Rossum <guido@python.org> writes:
Would it be wise to forbid ... as an enum value to preserve the option to use it for automatic value assignment in some indefinite future? Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On Sun, May 5, 2013 at 4:15 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
Correct. *If* we were to have a change of heart on this issue, we'd just introduce a class AutoNumberEnum. But I find the "..." syntax sufficiently ugly that I really don't expect I'll ever change my mind. -- --Guido van Rossum (python.org/~guido)
participants (13)
-
Antoine Pitrou
-
Barry Warsaw
-
Charles-François Natali
-
Eli Bendersky
-
Ethan Furman
-
Guido van Rossum
-
Nikolaus Rath
-
Paul Moore
-
Philip Jenvey
-
Stefan Behnel
-
Steven D'Aprano
-
Tim Delaney
-
Victor Stinner