[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
- Previous message: [Python-ideas] constant/enum type in stdlib
- Next message: [Python-ideas] constant/enum type in stdlib
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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.
Enum values are subclasses of int;
Only need to declare the enum key name;
Starts at zero by default;
Can change the start value;
Can have discontiguous values (e.g. 0, 1, 5, 6);
Can have other types of class attributes;
Ensures that there is a 1:1 mapping between key:value (throws an exception if either of these is violated;
Able to obtain the keys, values and items as per the mapping interface (sorted by value);
Lookup an enum by key or value;
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: %dand %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: %dand %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: %dand %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>
- Previous message: [Python-ideas] constant/enum type in stdlib
- Next message: [Python-ideas] constant/enum type in stdlib
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]