[Python-ideas] constant/enum type in stdlib (original) (raw)

Tim Delaney timothy.c.delaney at gmail.com
Thu Jan 31 02:27:24 CET 2013


On 31 January 2013 08:32, Terry Reedy <tjreedy at udel.edu> wrote:

On 1/30/2013 10:30 AM, Michael Foord wrote:

On 30 January 2013 15:22, Michael Foord

With a Python 3 metaclass that provides default values for *looked up* entries you could have this: class Color(Enum): RED, WHITE, BLUE The lookup would create the member - with the appropriate value. class values(dict): def init(self): self.value = 0 def getitem(self, key):

So RED, WHITE, BLUE are 1, 2, 3; not 0, 1, 2 as I and many readers might expect. That aside (which can be fixed), this is very nice.

Here is a version that I think creates an enum with most of the features of traditional and modern enums.

One thing to note is that any class attribute assigned a value which implements index will be considered an enum value assignment.

I've done some funky stuff to ensure that you can access all the above either via the enum class, or by an instance of the enum class. Most of the time you would just use the Enum subclass directly (i.e. it's a namespace) but there may be use cases for having instances of the Enum classes.

import collections import operator

class EnumValue(int): def new(cls, key, value): e = super().new(cls, value) super().setattr(e, 'key', key) return e

def __setattr__(self, key, value):
    raise TypeError("Cannot set attribute of type %r" % (type(self),))

def __repr__(self):
    return "<%s '%s': %d>" % (self.__qualname__, self.key, self)

class EnumValues(collections.OrderedDict): def init(self): super().init() self.value = 0 self.sealed = False

def __getitem__(self, key):
    try:
        obj = super().__getitem__(key)

        if not self.sealed and isinstance(obj, EnumValue):
            raise TypeError("Duplicate enum key '%s' with values: %d

and %d" % (obj.key, obj, self.value))

        return obj

    except KeyError:
        if key[:2] == '__' and key[-2:] == '__':
            raise

        value = self.value
        super().__setitem__(key, EnumValue(key, value))
        self.value += 1
        return value

def __setitem__(self, key, value):
    if key[:2] == '__' and key[-2:] == '__':
        return super().__setitem__(key, value)

    try:
        if isinstance(value, EnumValue):
            assert value.key == key
        else:
            value = operator.index(value)
    except TypeError:
        return super().__setitem__(key, value)

    try:
        o = super().__getitem__(key)

        if isinstance(o, EnumValue):
            raise TypeError("Duplicate enum key '%s' with values: %d

and %d" % (o.key, o, value))

    except KeyError:
        self.value = value + 1

        if isinstance(value, EnumValue):
            value = value
        else:
            value = EnumValue(key, value)

        super().__setitem__(value.key, value)

class EnumMeta(type):

@classmethod
def __prepare__(metacls, name, bases):
    return EnumValues()

def __new__(cls, name, bases, classdict):
    classdict.sealed = True
    result = type.__new__(cls, name, bases, dict(classdict))
    enum = []

    for v in classdict.values():
        if isinstance(v, EnumValue):
            enum.append(v)

    enum.sort()
    result._key_to_enum = collections.OrderedDict()
    result._value_to_enum = collections.OrderedDict()

    for e in enum:
        if e in result._value_to_enum:
            raise TypeError("Duplicate enum value %d for keys: '%s' and

'%s'" % (e, result._value_to_enum[e].key), e.key)

        if e.key in result._key_to_enum:
            raise TypeError("Duplicate enum key '%s' with values: %d

and %d" % (e.key, result._key_to_enum[e.key]), e)

        result._key_to_enum[e.key] = e
        result._value_to_enum[e] = e

    return result

def __getitem__(self, key):
    try:
        key = operator.index(key)
    except TypeError:
        return self._key_to_enum[key]
    else:
        return self._value_to_enum[key]

def _items(self):
    return self._key_to_enum.items()

def _keys(self):
    return self._key_to_enum.keys()

def _values(self):
    return self._key_to_enum.values()

def items(self):
    return self._items()

def keys(self):
    return self._keys()

def values(self):
    return self._values()

class Enum(metaclass=EnumMeta): def getitem(self, key): cls = type(self) return type(cls).getitem(cls, key)

def items(cls):
    return cls._items()

def keys(cls):
    return cls._keys()

def values(cls):
    return cls._values()

Enum.items = classmethod(Enum.items) Enum.keys = classmethod(Enum.keys) Enum.values = classmethod(Enum.values)

class Color(Enum): RED, WHITE, BLUE GREEN = 4 YELLOW ORANGE = 'orange' BLACK

def dump(self):
    print(self.RED, self.WHITE, self.BLUE, self.GREEN, self.YELLOW,

self.BLACK, self.ORANGE, self.dump)

print(Color.RED, Color.WHITE, Color.BLUE, Color.GREEN, Color.YELLOW, Color.BLACK, Color.ORANGE, Color.dump) Color().dump() print(repr(Color.RED)) print(repr(Color['RED'])) print(repr(Color().RED)) print(repr(Color()['RED'])) print(repr(Color[0])) print(repr(Color()[0])) print(*Color.items()) print(*Color().items()) print(*Color.keys()) print(*Color().keys()) print(*Color.values()) print(*Color().values())

Tim Delaney -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.python.org/pipermail/python-ideas/attachments/20130131/f2315725/attachment.html>



More information about the Python-ideas mailing list