[Python-Dev] PEP 435 -- Adding an Enum type to the Python standard library (original) (raw)
Guido van Rossum guido at python.org
Sat Apr 27 18:01:54 CEST 2013
- Previous message: [Python-Dev] PEP 435 -- Adding an Enum type to the Python standard library
- Next message: [Python-Dev] PEP 435 -- Adding an Enum type to the Python standard library
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
I've always found the nested class solution confusing when I had to use it in Django. It is a technical solution to a technical problem, but the resulting code is not very readable unless you have seen it a lot; it is a new, exceptional pattern.
And as for using 'in' instead of 'isinstance' to check whether a value belongs to a given enum class, that, too, is a deviation from normal practice, which will require special cases in frameworks provide e.g. type checking. (As I mentioned before, any framework using argument annotations to indicate the expected type(s) for arguments would have to special-case the checks for enums.)
Please let's try harder to come up with a way to separate enum value definitions from method definitions that works as expected in most common cases. I am willing to concede that it's hard to support things like class variables; maybe __private variables can be used for these; or the code can just use globals instead of class variables to hold per-class state. And init/new probably shouldn't be overridden. But instance methods, class methods, static methods, properties, and other decorated methods should all work, as should special methods like add or getitem.
Hm... A lightbulb just went off. Objects representing both undecorated and decorated methods have a get() method, courtesy of the descriptor protocol. Maybe checking for that will work? It feels Pythonic to me: it uses a corner of the language that most people don't even know exists (*), but it produces the desired effect in almost all cases that matter, the pattern is simple to describe and easy to use without thinking about it, and for experts the rules are completely clear, uncomplicated, and free of heuristics, so it is possible to reason about corner cases.
(*) Proof: even I didn't think of it until just now. :-)
On Sat, Apr 27, 2013 at 6:17 AM, Nick Coghlan <ncoghlan at gmail.com> wrote:
On Sat, Apr 27, 2013 at 7:41 AM, Guido van Rossum <guido at python.org> wrote:
On Fri, Apr 26, 2013 at 11:17 AM, Eli Bendersky <eliben at gmail.com> wrote:
In contrast, personally I feel the current proposal in PEP 435 has an appeal from the POV of simplicity. It really is a very nice separation of concerns between enum values and Enum as a container of such values. It even allows significant customization (IntEnum, etc) which is pretty simple to grok. It would be a shame to lose these for the sake of making Python a bit more like Java.
But it's not so much the "like Java" that matters to me. It's the realization that for the user who wants to define an enum type with some extra functionality, having a single class and putting the methods and the items in the same class is the simplest way to do it. The Java reference is just to point out that we're not exactly breaking new ground here. A common idiom in some other use cases (like ORMs) is to allow an inner class to customise behaviour beyond what the basic class syntax allows. It seems like that may be a good fit here, as a couple of simple changes should greatly simplify the process of tweaking the behaviour of the enum items, without adding more complexity to the implementation: 1. Change the name of "valuefactory" to something more concise like "enumitem". 2. Make EnumItem and IntEnumItem public classes (Barry has already noted that the current naming the associated classes in flufl.enum and PEP 435 isn't quite right, since the intended terminology is enum for the class, enum item for the labelled values, and value for the raw unlabelled objects, but the class names are currently EnumValue and IntEnumValue). Then, if you want to add custom behaviour to your enum, you would be able to do use a nested class relatively cleanly: class MyEnum(Enum): itemA = 1 itemB = 2 class enumitem(EnumItem): def init(self, enum, value, name): if not name.startswith("item"): raise ValueError("Item name {} doesn't start with 'item'".format(name)) super().init(enum, value, name) @property def letter(self): return self.name[4:] class MyExtendedEnum(MyEnum): # The custom enumitem is inherited along with the original attributes itemC = 3 itemD = 4 Furthermore, rather than tweaking isinstance checks, it may make more sense to support containment testing for enums, such that you can write things like: assert MyEnum.itemA in MyEnum assert 1 not in MyEnum Cheers, Nick. -- Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
-- --Guido van Rossum (python.org/~guido)
- Previous message: [Python-Dev] PEP 435 -- Adding an Enum type to the Python standard library
- Next message: [Python-Dev] PEP 435 -- Adding an Enum type to the Python standard library
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]