Subject: Yet another enum proposal :)
Date: Friday 22nd February 2013 19:25:08 UTC (over 5 years ago)
Ok, so at the risk of muddying the waters even more, I've put together yet another possible way to do enums, and would be interested to hear comments.. Based on the list of required/desired/undesired properties laid out in the enum PEP thread, I believe a lot of the back and forth to date has been due to the fact that all of the proposed implementations fall short of fulfilling at least some of the desired properties for a general-purpose enum implementation in different ways. I've put together something based on ideas from Tim's, Barry's, other things thrown out in discussion, and a few of my own which I think comes closer to ticking off most of the boxes. (The code and some examples are available at https://github.com/foogod/pyenum) Enums/groups are defined by creating subclasses of Enum, similarly to Barry's implementation. The distinction is that the base "Enum" class does not have any associated values (they're just singleton objects with names). At a basic level, the values themselves are defined like so: class Color (Enum): RED = __ GREEN = __ BLUE = __ As has been (quite correctly) pointed out before, the single-underscore (_) has a well-established meaning in most circles related to gettext/etc, so for this application I picked the next best thing, the double-underscore (__) instead. I think this is reasonably mnemonic as a "fill in the blank" placeholder, and also not unduly verbose. One advantage of using __ over, say, ellipsis (...) is that since it involves a name resolution, we can add (just a little!) magic to generate distinct __ objects on each reference, so, for example, the following actually works as the user probably expects it to: class Color (Enum): RED = CRIMSON = __ BLUE = __ (RED and CRIMSON actually become aliases referring to the same enum value, but BLUE is a different enum value) One other rather notable advantage to __ is that we can define a special multiplication behavior for it which allows us to make the syntax much more compact: class Color (Enum): RED, GREEN, BLUE, ORANGE, VIOLET, BEIGE, ULTRAVIOLET, ULTRABEIGE = __ * 8 (as an aside, I have an idea about how we might be able to get rid of the "* 8" altogether, but it requires a different (I think generally useful) change to the language which I will propose separately) Each enum value is actually an instance of its defining class, so you can determine what type of enum something is with simple inheritance checks: >>> isinstance(Color.RED, Color) True For enums which need to have int values, we can use IntEnum instead of Enum: class Errno (IntEnum): EPERM = 1 ENOENT = 2 ESRCH = 3 EINTR = 4 (Technically, IntEnum is just a special case of the more generic TypeEnum class: class IntEnum (TypeEnum, basetype=int): pass ..which means that theoretically you could define enums based on (almost) any base type (examples.py has an example using floats)) Anyway, as expected, enums have reasonable strs/reprs, and can be easily translated to/from base values using traditional "casting" syntax: >>> Errno.EPERM <__main__.Errno.EPERM (1)> >>> str(Errno.EPERM) 'EPERM' >>> int(Errno.EPERM) 1 >>> Errno(1) <__main__.Errno.EPERM (1)> You can also lookup enums by name using index notation: >>> Errno['EPERM'] <__main__.Errno.EPERM (1)> It's actually worth noting here that TypeEnums are actually subclasses of their basetype, so IntEnums are also ints, and can be used as drop-in replacements for any existing code which is expecting an int argument. They can also be compared directly as if they were ints: if exc.errno == Errno.EPERM: do_something() TypeEnums enforce uniqueness: >>> class Foo (IntEnum): ... A = 1 ... B = 1 ... Traceback (most recent call last): File "