Issue17961
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2013-05-12 10:57 by ncoghlan, last changed 2022-04-11 14:57 by admin. This issue is now closed.
Messages (25) | |||
---|---|---|---|
msg189013 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2013-05-12 10:57 | |
I encountered an interesting suggestion [1] regarding the enum.Enum convenience API: use the member names as their values, rather than the current integers starting from one. Since we're now using definition order rather than value order for iteration, this suggestion makes a lot of sense to me. (again, posting this as something to consider after the accepted PEP is incorporated) [1] http://www.acooke.org/cute/Pythonssad0.html |
|||
msg189022 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2013-05-12 12:28 | |
Specifically, my suggestion is that for auto-created enum members, the invariant "asset x.value == str(x)" should hold. In flufl.enum, using integers made sense because it relies on sorting of values to determine the iteration order. That's no longer a concern for the standard library version, as iteration has been changed to use definition order. |
|||
msg189023 - (view) | Author: Ethan Furman (ethan.furman) * ![]() |
Date: 2013-05-12 12:34 | |
In flufl.enum integers also made since as that was the default back-end data type. Currently, the functional method allows a type declaration. The behavior there could be tweaked such that no specification meant no value (a truly valueless enum!), type=int means ints starting from 1, type=str meant str values of names, any other value and you better had made your own enum derived class to support it. |
|||
msg189067 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-05-12 22:00 | |
On May 12, 2013, at 10:57 AM, Nick Coghlan wrote: >I encountered an interesting suggestion [1] regarding the enum.Enum >convenience API: use the member names as their values, rather than the >current integers starting from one. > >Since we're now using definition order rather than value order for iteration, >this suggestion makes a lot of sense to me. Cute, but I want the functional API to be equivalent to the class syntax. Besides: X = Enum('X', ((name, name) for name in 'ant bee cat dog elk'.split())) is not so much typing. |
|||
msg189072 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2013-05-12 23:24 | |
That gives the unqualified names, and I don't understand your other objection. The class syntax doesn't support implicit values at all. |
|||
msg189085 - (view) | Author: Ethan Furman (ethan.furman) * ![]() |
Date: 2013-05-13 00:35 | |
The class syntax (and default Enum) no longer have preferential treatment for integers (even __int__ is gone); so it is completely up to us as what should happen for the functional syntax. |
|||
msg189087 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-05-13 01:57 | |
On May 12, 2013, at 8:35 PM, Ethan Furman wrote: > Ethan Furman added the comment: > > The class syntax (and default Enum) no longer have preferential treatment for integers (even __int__ is gone); so it is completely up to us as what should happen for the functional syntax. Do you really think the enum discussion on python-dev was too short and could use another few thousand messages? ;) Or IOW, hasn't this already been decided by virtue of PEP acceptance? |
|||
msg189088 - (view) | Author: Ethan Furman (ethan.furman) * ![]() |
Date: 2013-05-13 02:06 | |
On 05/12/2013 06:57 PM, Barry A. Warsaw wrote: > > Do you really think the enum discussion on python-dev was too short and could use another few thousand messages? ;) No! > Or IOW, hasn't this already been decided by virtue of PEP acceptance? Indeed it has. I was merely replying to your comment about keeping class and function syntax in sync, when in fact there is no longer any direct correlation between the two other than the names used and the type created. |
|||
msg189094 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2013-05-13 04:09 | |
Accepting awkward implementation details when their rationale no longer applies doesn't make sense to me, no. The functional API originally used integers for good reasons. Over the course of the enum discussions, those reasons evaporated, but this wasn't noticed until after the PEP was accepted. However, the acceptance of the PEP is why I have proposed this as a post-incorporation change. |
|||
msg189115 - (view) | Author: Antoine Pitrou (pitrou) * ![]() |
Date: 2013-05-13 10:10 | |
I agree with Nick here, there's no reason to auto-number constants in Python. This is not C :-) |
|||
msg189132 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-05-13 14:01 | |
On May 13, 2013, at 10:10 AM, Antoine Pitrou wrote: >I agree with Nick here, there's no reason to auto-number constants in >Python. This is not C :-) Why should they be strings? Why not object()? Why is `x.value == str(x)` a useful invariant to hold? |
|||
msg189135 - (view) | Author: Antoine Pitrou (pitrou) * ![]() |
Date: 2013-05-13 14:06 | |
> >I agree with Nick here, there's no reason to auto-number constants > >in > >Python. This is not C :-) > > Why should they be strings? Why not object()? Because strings are readable, I'd say. |
|||
msg189137 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2013-05-13 14:24 | |
On 14 May 2013 00:06, "Antoine Pitrou" <report@bugs.python.org> wrote: > > > Antoine Pitrou added the comment: > > > >I agree with Nick here, there's no reason to auto-number constants > > >in > > >Python. This is not C :-) > > > > Why should they be strings? Why not object()? > > Because strings are readable, I'd say. Yep. Since we no longer have a compelling reason for it to be anything else, it may as well be the human readable string. |
|||
msg189140 - (view) | Author: Ethan Furman (ethan.furman) * ![]() |
Date: 2013-05-13 14:44 | |
So the repr will look like: <Color.red: 'red'> ? |
|||
msg189142 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-05-13 15:09 | |
On May 13, 2013, at 02:06 PM, Antoine Pitrou wrote: >Antoine Pitrou added the comment: > >> >I agree with Nick here, there's no reason to auto-number constants >> >in >> >Python. This is not C :-) >> >> Why should they be strings? Why not object()? > >Because strings are readable, I'd say. The repr would then be <Color.red: Color.red> Yuck. Also, you would have to allow for subclasses (e.g. IntEnum) to override auto-assignment. Clearly, you can't use strings for X = IntEnum('X', 'start middle end') |
|||
msg189143 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-05-13 15:10 | |
On May 13, 2013, at 02:24 PM, Nick Coghlan wrote: >Yep. Since we no longer have a compelling reason for it to be anything >else, it may as well be the human readable string. Again, why does it matter? That's the whole point of having a human readable str() and repr(). |
|||
msg191449 - (view) | Author: Ethan Furman (ethan.furman) * ![]() |
Date: 2013-06-19 06:45 | |
Enum members have two pieces of easily accessible information: `name` and `value`. The name is taken from the attribute the value is assigned to; the value, obviously, is the value assigned. The question, then, is what should happen when the function syntax is used, and values are not explicitly given? - use `int`s starting from one - use `int`s starting from zero - use the key name itself - use no value at all Working from the bottom up: - no value: if the enum member has no actual value, the user cannot retrieve the member using the class call syntax (e.g. Color(???) ) - the key name as value: if we go this route, then instead of two pieces of info, our enum member has only one piece in two places - `int`s from zero: falls in line with normal Python counting, but is the zeroth member False? - `int`s from one: avoids the False question, and it's what flufl.enum does. No value is obviously a no-go as it causes more problems than it solves. `str` value is also a no-go as the key name is already available as `name`. `int`s starting from zero or starting from one? Personally, I have no problem with a zero-value enum member that is True -- not every zero should be False. This is the only change I would be willing to make. To sum up: the name is already available in the name, no need to have it be the value as well. I am open to changing the start value to zero instead of one (which is what I do in my customization of Enum in my personal modules ;) . |
|||
msg191460 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2013-06-19 11:32 | |
OK, I've satisfied myself that the current behaviour is reasonable, and it's specifically the subclassing behaviour of the status quo that works for me. 1. You have to specifically access the "x.value" attribute of a default enum member to see it. "str(x)" shows the fully qualified name, not the value (even for concrete subclasses). 2. The implicitly chosen values are valid for any concrete enum subclass that accepts a single integer as an argument. This covers strings and the whole numeric tower. The same can't be said for using the enum name. 3. The implicitly chosen values are always "true", even when used with a concrete numeric enum subclass. This matches the behaviour of standard user defined classes (where all instances are considered true by default unless you define __len__ or __bool__ to say otherwise). The fact "str(x)" returns the fully qualified name for IntEnum worries me a bit, but if there's a real backwards compatibility problem there (rather than a theoretical one), hopefully we'll see it once we start converting socket and errno. |
|||
msg191462 - (view) | Author: Eli Bendersky (eli.bendersky) * ![]() |
Date: 2013-06-19 12:56 | |
Nicely done summary, Ethan. I think it would be worthwhile to add the reasoning of "why from 1 and not from 0" into the documentation and maybe the PEP too. I think the "falsiness" answer is reasonable, and since it's a common FAQ it's good to state it explicitly. But make sure to specify that it only applies to IntEnum because Enum values are always truthy :) |
|||
msg191464 - (view) | Author: Eli Bendersky (eli.bendersky) * ![]() |
Date: 2013-06-19 13:00 | |
> The fact "str(x)" returns the fully qualified name for IntEnum worries me a bit, but if there's a real backwards compatibility problem there (rather than a theoretical one), hopefully we'll see it once we start converting socket and errno. What is the theoretical problem here? I though that it's an explicit design goal of enums? Which RED - Color.RED, or MeatReadiness.RED? For sockets: >>> class SocketType(IntEnum): ... SOCK_STREAM = 1 ... SOCK_DGRAM = 2 ... >>> str(SocketType.SOCK_STREAM) 'SocketType.SOCK_STREAM' Looks pretty good to me in terms of debuggability. |
|||
msg191465 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-06-19 13:31 | |
On Jun 19, 2013, at 01:00 PM, Eli Bendersky wrote: >What is the theoretical problem here? I though that it's an explicit design >goal of enums? Which RED - Color.RED, or MeatReadiness.RED? For sockets: > >>>> class SocketType(IntEnum): >... SOCK_STREAM = 1 >... SOCK_DGRAM = 2 >... >>>> str(SocketType.SOCK_STREAM) >'SocketType.SOCK_STREAM' > >Looks pretty good to me in terms of debuggability. Me too. |
|||
msg191466 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-06-19 13:41 | |
On Jun 19, 2013, at 06:45 AM, Ethan Furman wrote: >To sum up: the name is already available in the name, no need to have it be >the value as well. I am open to changing the start value to zero instead of >one (which is what I do in my customization of Enum in my personal modules ;) I'm sure it's obvious that I prefer start-from-one, and aside from the truthiness question, it would maximize compatibility with flufl.enum. Besides, the functional API accepts a sequence so if you *really* wanted start-from-zero, then you can do it easily. |
|||
msg191468 - (view) | Author: Alyssa Coghlan (ncoghlan) * ![]() |
Date: 2013-06-19 13:48 | |
I created issue 18264 after I tried it and found my theoretical concern wasn't theoretical at all: swapping a true integer for the current incarnation of enum.IntEnum breaks (at least) JSON serialisation, which means we can't use it in its current form to replace stdlib constants. |
|||
msg191469 - (view) | Author: Barry A. Warsaw (barry) * ![]() |
Date: 2013-06-19 13:54 | |
On Jun 19, 2013, at 01:48 PM, Nick Coghlan wrote: >I created issue 18264 after I tried it and found my theoretical concern >wasn't theoretical at all: swapping a true integer for the current >incarnation of enum.IntEnum breaks (at least) JSON serialisation, which means >we can't use it in its current form to replace stdlib constants. JSON has to be taught how to serialize enums. Of course, it also has to be taught how to serialize datetimes, timedeltas, and other common data types. In Mailman, I use the following subclass of json.JSONEncoder. Note though that in my REST API, I always know the class/enum that a string should be associated with so my deserializer always does the right thing. class ExtendedEncoder(json.JSONEncoder): """An extended JSON encoder which knows about other data types.""" def default(self, obj): if isinstance(obj, datetime): return obj.isoformat() elif isinstance(obj, timedelta): # as_timedelta() does not recognize microseconds, so convert these # to floating seconds, but only if there are any seconds. if obj.seconds > 0 or obj.microseconds > 0: seconds = obj.seconds + obj.microseconds / 1000000.0 return '{0}d{1}s'.format(obj.days, seconds) return '{0}d'.format(obj.days) elif isinstance(obj, Enum): # It's up to the decoding validator to associate this name with # the right Enum class. return obj.name return json.JSONEncoder.default(self, obj) |
|||
msg191472 - (view) | Author: Antoine Pitrou (pitrou) * ![]() |
Date: 2013-06-19 14:07 | |
> JSON has to be taught how to serialize enums. Of course, it also has > to be > taught how to serialize datetimes, timedeltas, and other common data > types. How so? The point of IntEnum was that it derived from int, and therefore would avoid any need for special-casing in C code. (now if json has a PyLong_CheckExact() check, perhaps it can be relaxed to PyLong_Check()...) |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:57:45 | admin | set | github: 62161 |
2013-06-19 14:07:53 | pitrou | set | messages: + msg191472 |
2013-06-19 13:54:54 | barry | set | messages: + msg191469 |
2013-06-19 13:48:14 | ncoghlan | set | messages: + msg191468 |
2013-06-19 13:41:29 | barry | set | messages: + msg191466 |
2013-06-19 13:31:29 | barry | set | messages: + msg191465 |
2013-06-19 13:00:52 | eli.bendersky | set | messages: + msg191464 |
2013-06-19 12:56:12 | eli.bendersky | set | messages: + msg191462 |
2013-06-19 11:32:02 | ncoghlan | set | status: open -> closed resolution: rejected messages: + msg191460 stage: needs patch -> resolved |
2013-06-19 06:45:43 | ethan.furman | set | nosy:
+ eli.bendersky title: Use enum names as values in enum.Enum convenience API -> Use enum names as values in enum.Enum() functional API messages: + msg191449 assignee: ethan.furman |
2013-05-13 15:10:20 | barry | set | messages: + msg189143 |
2013-05-13 15:09:31 | barry | set | messages: + msg189142 |
2013-05-13 14:44:28 | ethan.furman | set | messages: + msg189140 |
2013-05-13 14:24:41 | ncoghlan | set | messages: + msg189137 |
2013-05-13 14:06:28 | pitrou | set | messages: + msg189135 |
2013-05-13 14:01:24 | barry | set | messages: + msg189132 |
2013-05-13 10:10:58 | pitrou | set | nosy:
+ pitrou messages: + msg189115 |
2013-05-13 04:09:05 | ncoghlan | set | messages: + msg189094 |
2013-05-13 02:06:04 | ethan.furman | set | messages: + msg189088 |
2013-05-13 01:57:08 | barry | set | messages: + msg189087 |
2013-05-13 00:35:53 | ethan.furman | set | messages: + msg189085 |
2013-05-12 23:24:21 | ncoghlan | set | messages: + msg189072 |
2013-05-12 22:00:29 | barry | set | messages: + msg189067 |
2013-05-12 21:57:15 | barry | set | nosy:
+ barry |
2013-05-12 12:34:06 | ethan.furman | set | messages: + msg189023 |
2013-05-12 12:28:56 | ncoghlan | set | messages: + msg189022 |
2013-05-12 11:12:31 | ethan.furman | set | nosy:
+ ethan.furman |
2013-05-12 10:57:35 | ncoghlan | set | dependencies:
+ Code, test, and doc review for PEP-0435 Enum versions: + Python 3.4 |
2013-05-12 10:57:02 | ncoghlan | create |