[Python-Dev] PEP 435 -- Adding an Enum type to the Python standard library

Ethan Furman ethan at stoneleaf.us
Fri Apr 26 07:07:37 CEST 2013


On 04/25/2013 07:13 PM, Nick Coghlan wrote:
> On Fri, Apr 26, 2013 at 8:29 AM, Barry Warsaw <barry at python.org> wrote:
>> On Apr 25, 2013, at 03:19 PM, Guido van Rossum wrote:
>>
>>> Clearly this is a trick question. :-)
>>
>> A bit, yes. :)
>>
>>> I was told when this was brought up previously (a week ago?) that it
>>> would be simple to make it truly the same class.
>>
>> It didn't sound simple to me, but I haven't seen any actual code yet.
>
> I'm the one who said I didn't see any obvious barriers to the merger,
> but I've realised there is one, and it's similar to one namedtuple
> struggles with: how to handle method definitions.
>
> [snip]
>
> With a merged design, it becomes *really* hard to give the instances
> custom behaviour, because the metaclass will somehow have to
> differentiate between namespace entries that are intended to be
> callables, and those which are intended to be instances of the enum.
> This is not an easy problem to solve.

I'm probably going to regret asking this, but what's difficult with the following?

8<-----------------------------------------------------------------------------------------
class EnumDict(dict):
     """
     automatically assigns the next _int for an enum
     """

     def __init__(yo):
         super().__init__()
         yo._allow_duplicates = False
         yo._value = 1
         yo._base = 1
         yo._enums = []
         yo._type = None # object means enum, anything else means all must be of that type

     def __setitem__(yo, key, new_value):
         """
         main purpose is to support auto-numbering of members
         """
         existing = yo.get(key)
         if type(existing) is attrs:  # attrs is a helper class
             raise TypeError('Attempted to reuse key: %s' % key)
         if not key[:2] == key[-2:] == '__':
             old_value = None
             if isinstance(new_value, attrs):
                 old_value = new_value
                 if new_value.integer is None:
                     new_value = yo._value
                 else:
                     new_value = new_value.integer
                     if not isinstance(new_value, int):
                         raise TypeError(
                             "an enum integer must be an instance of type <int>, not %s"
                             % type(new_value)
                             )
             if not callable(new_value):  # this if-else is probably not final
                 if (isinstance(new_value, int) and old_value is not None
                 or yo._type in (object, )):
                     yo._check_duplicate_integer(new_value, key)
                     yo._value = new_value
                     yo._inc_integer()
                     yo._enums.append(key)
                     new_value = attrs(integer=new_value)
                 elif yo._type is None: # random bunch of named constants
                     yo._check_duplicate_integer(new_value, key)
                     value = yo._value
                     yo._inc_integer()
                     yo._enums.append(key)
                     new_value = attrs(value=new_value, integer=value)
                 elif isinstance(new_value, yo._type): # single type of named constants
                     if isinstance(yo._type, int):
                         value = new_value
                     else:
                         value = yo._value
                     yo._check_duplicate_integer(value, key)
                     yo._inc_integer()
                     yo._enums.append(key)
                     new_value = attrs(value=new_value, integer=value)


             if old_value is not None:
                 new_value, old_value.integer = old_value, new_value.integer
         dict.__setitem__(yo, key, new_value)

     def _inc_integer(yo):
         if yo._base == 1:
             yo._value += 1
         else:
             if yo._value == 0:
                 value = 1
             else:
                 value = floor(log(yo._value, 2))
                 value = 2 ** value
                 value <<= 1
             yo._value = value

     def _check_duplicate_integer(yo, new_value, key):
         for name, value in yo.items():
             if not isinstance(value, attrs):
                 continue
             if value.integer == new_value and name != key and not yo._allow_duplicates:
                 raise TypeError('duplicate value for %s: %d' % (key, value))

8<-------------------------------------------------------------------------------------------

Basically, if the assigned value is not `attrs` or a literal of the same type as the enum, the metaclass ignores it.

--
~Ethan~


More information about the Python-Dev mailing list