
There is a several-month-old request to add aenum's [1] AutoNumberEnum to the stdlib [2]. The requester and two of the three developers of Enum are in favor (the third hasn't chimed in yet). This new addition would enable the following: from Enum import AutoNumberEnum class Color(AutoNumberEnum): # auto-number magic is on Red Green Blue Cyan # magic turns off when non-enum is defined def is_primary(self): # typos in methods, etc, will raise return self in (self.Red, self.Grene, self.Blue) # typos after the initial definition stanza will raise BlueGreen = Blue + Grene There is, of course, the risk of typos during the initial member definition stanza, but since this magic only happens when the user explicitly asks for it (AutoNumberEnum), I think it is acceptable. The `start` parameter is still available, and assigning a number is supported (subsequent numbers will (re)start from the assigned number). Thoughts? Opinions? Flames? -- ~Ethan~ [1] https://pypi.python.org/pypi/aenum [2] http://bugs.python.org/issue26988

On Wed, 29 Jun 2016 at 10:41 Ethan Furman <ethan@stoneleaf.us> wrote:
Is it going to subclass Enum or IntEnum? Personally I would be quite happy to never have to specify a value for enums ever again, but only if they subclass Enum (since IntEnum is for compatibility with C stuff where a specific value is needed I don't think users need to mess that up by having the automatic numbering not work how they would expect).

On 06/29/2016 12:11 PM, Guido van Rossum wrote:
And how would you implement that without support from the compiler? Does it use a hook that catches the NameError?
It's built into the _EnumDict class dictionary used during class creation. Current (edited) code from the aenum package that implements this: class _EnumDict(dict): """Track enum member order and ensure member names are not reused. EnumMeta will use the names found in self._member_names as the enumeration member names. """ def __init__(self, locked=True, start=1, multivalue=False): super(_EnumDict, self).__init__() # list of enum members self._member_names = [] # starting value for AutoNumber self._value = start - 1 # when the magic turns off self._locked = locked ... def __getitem__(self, key): if ( self._locked or key in self or _is_sunder(key) or _is_dunder(key) ): return super(_EnumDict, self).__getitem__(key) try: # try to generate the next value value = self._value + 1 self.__setitem__(key, value) return value except: # couldn't work the magic, report error raise KeyError('%s not found' % key) def __setitem__(self, key, value): """Changes anything not sundured, dundered, nor a descriptor. Single underscore (sunder) names are reserved. """ if _is_sunder(key): raise ValueError('_names_ are reserved for future Enum use') elif _is_dunder(key): if key == '__order__': key = '_order_' if _is_descriptor(value): self._locked = True elif key in self._member_names: # descriptor overwriting an enum? raise TypeError('Attempted to reuse name: %r' % key) elif not _is_descriptor(value): if key in self: # enum overwriting a descriptor? raise TypeError('%s already defined as: %r' % ... self._member_names.append(key) if not self._locked: if isinstance(value, int): self._value = value else: count = self._value + 1 self._value = count value = count, value else: # not a new member, turn off the autoassign magic self._locked = True super(_EnumDict, self).__setitem__(key, value) Disclaimer: some errors may have crept in as I deleted unrelated content. For the full code check out the _EnumDict class in the aenum package. -- ~Ethan~

On 06/29/2016 01:01 PM, Ivan Levkivskyi wrote:
Why would you want that? I remind you that this descends from Enum, so its members won't be directly interchangeable with ints. Presumably you want a bitfield enum, and those should descend from IntEnum. TBH I'd prefer the AutoNumberEnum *not* have this feature; it's already a little too magical for my tastes. //arry/

Presumably you want a bitfield enum, and those should descend from IntEnum.
Yes, and probably having an AutoNumberIntEnum would indeed be too much magic in one place. Anyway, it is easy to implement bitfield IntEnum without magic. To be clear, I like the Ethan's original proposal. -- Ivan

On 06/29/2016 03:40 PM, Roberto Martínez wrote:
Why the 'start' parameter default is 1? 0 (zero) is more consistent with other parts of the language: indexes, enumerate, range...
An excerpt from [1]:
The reason for defaulting to 1 as the starting number and not 0 is that 0 is False in a boolean sense, but enum members all evaluate to True.
-- ~Ethan~ [1] https://docs.python.org/3/library/enum.html#functional-api

It may be worth mentioning that pandas Categoricals are mutable and zero-based: https://pandas-docs.github.io/pandas-docs-travis/categorical.html Serialization to SQL and CSV is (also?) lossy, though: - https://pandas-docs.github.io/pandas-docs-travis/categorical.html#getting-da... - https://pandas-docs.github.io/pandas-docs-travis/io.html#io-stata-categorica... On 06/29/2016 03:40 PM, Roberto Martínez wrote: Why the 'start' parameter default is 1? 0 (zero) is more consistent with
other parts of the language: indexes, enumerate, range...
An excerpt from [1]: The reason for defaulting to 1 as the starting number and not 0 is that 0
is False in a boolean sense, but enum members all evaluate to True.
-- ~Ethan~ [1] https://docs.python.org/3/library/enum.html#functional-api _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/wes.turner%40gmail.com

On Wed, 29 Jun 2016 at 10:41 Ethan Furman <ethan@stoneleaf.us> wrote:
Is it going to subclass Enum or IntEnum? Personally I would be quite happy to never have to specify a value for enums ever again, but only if they subclass Enum (since IntEnum is for compatibility with C stuff where a specific value is needed I don't think users need to mess that up by having the automatic numbering not work how they would expect).

On 06/29/2016 12:11 PM, Guido van Rossum wrote:
And how would you implement that without support from the compiler? Does it use a hook that catches the NameError?
It's built into the _EnumDict class dictionary used during class creation. Current (edited) code from the aenum package that implements this: class _EnumDict(dict): """Track enum member order and ensure member names are not reused. EnumMeta will use the names found in self._member_names as the enumeration member names. """ def __init__(self, locked=True, start=1, multivalue=False): super(_EnumDict, self).__init__() # list of enum members self._member_names = [] # starting value for AutoNumber self._value = start - 1 # when the magic turns off self._locked = locked ... def __getitem__(self, key): if ( self._locked or key in self or _is_sunder(key) or _is_dunder(key) ): return super(_EnumDict, self).__getitem__(key) try: # try to generate the next value value = self._value + 1 self.__setitem__(key, value) return value except: # couldn't work the magic, report error raise KeyError('%s not found' % key) def __setitem__(self, key, value): """Changes anything not sundured, dundered, nor a descriptor. Single underscore (sunder) names are reserved. """ if _is_sunder(key): raise ValueError('_names_ are reserved for future Enum use') elif _is_dunder(key): if key == '__order__': key = '_order_' if _is_descriptor(value): self._locked = True elif key in self._member_names: # descriptor overwriting an enum? raise TypeError('Attempted to reuse name: %r' % key) elif not _is_descriptor(value): if key in self: # enum overwriting a descriptor? raise TypeError('%s already defined as: %r' % ... self._member_names.append(key) if not self._locked: if isinstance(value, int): self._value = value else: count = self._value + 1 self._value = count value = count, value else: # not a new member, turn off the autoassign magic self._locked = True super(_EnumDict, self).__setitem__(key, value) Disclaimer: some errors may have crept in as I deleted unrelated content. For the full code check out the _EnumDict class in the aenum package. -- ~Ethan~

On 06/29/2016 01:01 PM, Ivan Levkivskyi wrote:
Why would you want that? I remind you that this descends from Enum, so its members won't be directly interchangeable with ints. Presumably you want a bitfield enum, and those should descend from IntEnum. TBH I'd prefer the AutoNumberEnum *not* have this feature; it's already a little too magical for my tastes. //arry/

Presumably you want a bitfield enum, and those should descend from IntEnum.
Yes, and probably having an AutoNumberIntEnum would indeed be too much magic in one place. Anyway, it is easy to implement bitfield IntEnum without magic. To be clear, I like the Ethan's original proposal. -- Ivan

On 06/29/2016 03:40 PM, Roberto Martínez wrote:
Why the 'start' parameter default is 1? 0 (zero) is more consistent with other parts of the language: indexes, enumerate, range...
An excerpt from [1]:
The reason for defaulting to 1 as the starting number and not 0 is that 0 is False in a boolean sense, but enum members all evaluate to True.
-- ~Ethan~ [1] https://docs.python.org/3/library/enum.html#functional-api

It may be worth mentioning that pandas Categoricals are mutable and zero-based: https://pandas-docs.github.io/pandas-docs-travis/categorical.html Serialization to SQL and CSV is (also?) lossy, though: - https://pandas-docs.github.io/pandas-docs-travis/categorical.html#getting-da... - https://pandas-docs.github.io/pandas-docs-travis/io.html#io-stata-categorica... On 06/29/2016 03:40 PM, Roberto Martínez wrote: Why the 'start' parameter default is 1? 0 (zero) is more consistent with
other parts of the language: indexes, enumerate, range...
An excerpt from [1]: The reason for defaulting to 1 as the starting number and not 0 is that 0
is False in a boolean sense, but enum members all evaluate to True.
-- ~Ethan~ [1] https://docs.python.org/3/library/enum.html#functional-api _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/wes.turner%40gmail.com
participants (7)
-
Brett Cannon
-
Ethan Furman
-
Guido van Rossum
-
Ivan Levkivskyi
-
Larry Hastings
-
Roberto Martínez
-
Wes Turner