[Python-Dev] [Python-checkins] peps: Pre-alpha draft for PEP 435 (enum). The name is not important at the moment, as (original) (raw)
Eric Snow ericsnowcurrently at gmail.com
Thu Feb 28 10:56:07 CET 2013
- Previous message: [Python-Dev] [Python-checkins] peps: Pre-alpha draft for PEP 435 (enum). The name is not important at the moment, as
- Next message: [Python-Dev] [Python-checkins] peps: Pre-alpha draft for PEP 435 (enum). The name is not important at the moment, as
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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:
- the items
- interaction between grouped items
- the grouping itself
- 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
- Previous message: [Python-Dev] [Python-checkins] peps: Pre-alpha draft for PEP 435 (enum). The name is not important at the moment, as
- Next message: [Python-Dev] [Python-checkins] peps: Pre-alpha draft for PEP 435 (enum). The name is not important at the moment, as
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]