Having had some time to think about this problem space, here's my take on it:
=============================== The problem-space can be broken down into four layers: 1. the items 2. interaction between grouped items 3. the grouping itself 4. conversion from a class to a group Here are *potential* characteristics at each layer: items ----- * immutable * unique * proxies or otherwise implicitly exposes underlying value * or only explicitly exposes value * or *is* the value with extra attributes added on * int-like * immutable underlying values * hashable * repr shows group name, item name, and underlying value * str shows just item name * value-less * a class that subclasses the group items and item interaction -------------------------- * equality comparible * richly comparable * comparison by identity only * interaction restricted to within group * flag-like (bitwise operations, etc.) groups ------ * iterable * sized * ordered (sortable) * inheritance to extend the group * a class rather than an instance of an enum type * query: name -> value * immutable * immutable underlying collection of items * allow aliases for actual items * group itself is named * has __qualname__ * repr shows items * no duplicates * a sequence without semantically meaningful values * unserialize (value -> item) using call syntax on group conversion ---------- * inherit a base enum type * use a class decorator * use a factory function, perhaps not even on a class (for more dynamic enum creation) * __getitem__() trick for defining value-less items * auto-numbering * explicit values * implicit values using ... * filter out "private" names * use only upper-case names There is a danger in trying to make an "enum" that is capable of doing everything. People need a simple common ground. When an object is an enum, a developer should be able to know immediately how to interact with it and that interaction should have a small cross-section. =============================== With that in mind and considering a lot of the discussion that has gone on, here's an API that tries to find a middle ground: "items" NamedValue #Nick's recipe NamedItem #an opaque named wrapper around a value group # the "enum" to which the item belongs value # the wrapped value __qualname__ # group qualname + item name __repr__ -> class + qualname + value __str__ -> item name __eq__ -> NotImplemented expose(ns) # export the value out to any namespace OrderedItem(NamedItem) # a totally ordered item __eq__ -> other is self __lt__ -> ask self.group to decide FlagItem(NamedItem) # a bitwise/set operation compatible item __and__ -> ask self.group to do it ... FlagsItem(FlagItem) # the result of FlagItem bitwise operations __repr__ -> shows the the items or'ed together items # the items that were joined together by the operation IntItem(NamedItem) # for those that really need a named item to be a little less opaque __int__ __index__ __bool__ " groups" Group __name__ __qualname__ __items__ # the underlying collection of items (tuple or frozenset) _ns # a GroupValues instance for the group __repr__ -> group name + items __call__(value) -> item # essentially the unserializer for an item _expose(ns) # calls expose() on each item in the group OrderedGroup # corresponds to OrderedItem FlagGroup # corresponds to FlagItem GroupValues # a Mapping proxy around a group's (name -> value) "conversion" named_values_from_class() -> {name: value} # simply extract the values from the class auto_int_from_class() # a classic enum auto_bin_from_class() # similar but steps in powers of 2 as_enum(*, ordered=False, flagged=False, converter=..., strict=True, **kwargs) # the main class decorator factory Examples: @as_enum() class Spam: A = ... B = ... C = ... Spam.A._expose(globals()) @as_enum(converter=auto_bin_from_class, flagged=True) class Ham: A = 1 B = ... C = ... D = 32 Ham.A == 1 # False Ham.B | Ham.C # a FlagsItem containing the two iter(Ham) # TypeError iter(Ham.__items__) # the items iter(Ham._ns) # the names iter(Ham._ns.values()) # the values Key points: * uses a class decorator rather than inheritance to convert a class into an enum * the objects make use of __qualname__ as much as is reasonable * the various Item classes are meant to be compatible (e.g. multiple inheritance) * groups and items are immutable * if order matters, using a class decorator requires that classes use OrderedDict by default for their namespace, this is a feasible change once we have an OrderedDict written in C (issue #16991) * value-less NamedItems are totally valid and the use of ... in converters would support their creation * the Group API is very simple (what you are using over and over) * the definition/conversion API is a little thicker with the basic case simplified * NamedItems are simply opaque wrappers (with the mild exception of IntItem) * Nick's NamedValue is utterly transparent and yet compatible with use in a Group In the interest of having something more concrete, a very raw and still relatively non-functional implementation of the above API can be found at: https://bitbucket.org/ericsnowcurrently/odds_and_ends/src/default/enum.py Finally, like I said before, the smaller the API the better. Obviously what I've got here could be distilled. However, it does capture the way I see the separate layers here. -eric _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com