PEP for enum library type?

Hi all, The ideas thrown around and Tim's prototype in the thread "constant/enum type in stdlib" show that it's possible to use some metaclass magic to implement very convenient syntax for enums, while only including them in the stdlib (no language changes): from enum import Enum class Color(Enum): RED, BLUE, GREEN I'm, for one, convinced that with this in hand we should *not* add special syntax for enums, since the gains would be minimal over the above proposal. While the parallel thread (and Tim's Bitbucket issue tracker) discusses his proposed implementation, I think it may be worthwhile to start from the other direction by writing a PEP that aims to include this in 3.4. The way I see it, the PEP should discuss and help us settle upon a minimal set of features deemed important in an enum. If that sounds OK to people, then someone should write that PEP :-) This could be Tim, or Barry who's been maintaining flufl.enum for a long time. If no one steps up, I will gladly do it. Thoughts? Eli

Le Tue, 12 Feb 2013 07:21:04 -0800, Eli Bendersky <eliben@gmail.com> a écrit :
I think I'd favour a namedtuple-style approach, e.g.: class Color(Enum): fields = ('RED', 'BLUE', 'GREEN') (which allows you to build enums programmatically too) Also, I think you have to provide several classes, at least SequenceEnum and StringEnum, and perhaps also BitmaskEnum. It makes sense for them to be separate types, 1) because it makes it explicit how the given enum behaves, 2) because combining integer and str constants in an enum would be crazy. Regards Antoine.

On 02/12/2013 07:31 AM, Antoine Pitrou wrote:
I partially agree with Antoine; there should be three types of Enum available: - sequence (most common: 0, 1, 2, 3, etc.) - flag (1, 2, 4, 8, etc.) - unique ('on', 'off'; 'north', 'south', 'east', 'west') and they shouldn't be mixed together higgledy-piggledy in the same class. However, I like the simplicity of Tim's approach. The result of the implementation I've been playing with looks something like: class Color(Enum): type = 'sequence' RED, GREEN, BLUE class Geometry(Enum): type = 'unique' LEFT, RIGHT, TOP, BOTTOM WEST, EAST, NORTH, SOUTH class FieldType(Enum): type = 'flag' BINARY, AUTOINC, NULLABLE The metaclass takes care of assigning the next value and selecting the proper subclass; 'unique' values are lower-cased (LEFT -> 'left'); subclassing is possible; and each Enum value is properly an instance of it's Enum class. One important reason for having the different types of Enum is for prototyping. -- ~Ethan~

I'm torn. I like the clean look of Tim's: class Flag(Enum): RED, WHITE, BLUE However, I don't like the magic needed for its implementation -- and anybody who thinks they know Python pretty well and thinks about this syntax for more than two seconds has to wonder how on earth it's done (and the answer is pretty horrible). It is also pretty brittle to depend on the lookup order -- and I suspect it will be prone to masking other bugs (any typo in a name used in class-level code will essentially generate a bogus new enum value). OTOH I don't like having the string quotes of Antoine's counter-proposal: class Flag(Enum): fields = ('RED', 'WHITE', 'BLUE') Whenever I see quoted names that are also used unquoted, I cringe a little (and this includes calls to hasattr() or getattr() with a string literal for the name). The compromise that I would personally lean towards is more like this: class Flag(Enum): RED, WHITE, BLUE = <something> Even if the <something> requires you to know how many values you are defining, like range(3). If we have to have a somewhat more verbose syntax that doesn't bother me too much. FWIW I do like being able to define enums that are ints and strings (but not mixed); masks/flags I see more as a special case of ints (if you use this you are likely trying to match some API defined in C or C++). I also agree that it must be possible to customize the enum class and the behavior of the values by defining methods on the class. -- --Guido van Rossum (python.org/~guido)

On 02/12/2013 10:08 AM, Guido van Rossum wrote:
It seems to me the point of an enum is to give names to an order, so why would the lookup order be a problem?
Having just dealt with using a poor-person's Enum (THIS, THAT, THE_OTHER = range(3)) and then having to update it a few times (including all the places in the code that depended on the length), I can safely say that the range(n) construct is a PITA. If you don't mind verbose, we could always go for something like: class Color(Enum): start_defs() BLACK RED, GREEN, BLUE CYAN, YELLOW, MAGENTA end_defs() and if we mandate that the enum names must come first we can even ditch the 'start_defs' call. As far as typos go, I don't think that's a new problem (it certainly isn't for me, anyway ;) and my best defense is plenty of tests. -- ~Ethan~

On Tue, Feb 12, 2013 at 10:31 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Because the language requirement that the expressions in a tuple are evaluated left-to-right is fairly weak. (There are other, similar contexts where the order is not l2r.)
If that's where you're leaning I would much rather do this: class Color(Enum): BLACK = val() RED = val() # etc. EIBTI.
As far as typos go, I don't think that's a new problem (it certainly isn't for me, anyway ;) and my best defense is plenty of tests.
So with Tim's implementation, what happens here: class Color(Enum): RED, GREEN, BLUE if sys.platform == 'win32': MAGENTA and you happen to have no "import sys" in your module? The sys lookup will succeed, create a new enum, and then you will get an error something like this: AttributeError: 'Enum' object has no attribute 'platform' -- --Guido van Rossum (python.org/~guido)

On 02/12/2013 11:13 AM, Guido van Rossum wrote:
Ah.
Personally, I'd be fine with: class Color(Enum): BLACK RED BLUE PURPLE which avoids the l2r issue, and the repetitive use of val() (or whatever it's called).
We could minimize that issue by requiring that enum names be ALLCAPS -- if something comes through that isn't, don't supply a value and let the AttributeError perculate up. -- ~Ethan~

On 12 February 2013 19:26, Ethan Furman <ethan@stoneleaf.us> wrote:
Can you force people not to use: class Color(Enum): BLACK, RED, BLUE, PURPLE ? If I'm understanding the implementation, if you support one then you support the other. Hence the problem will still stand. That said, I'm not personally too worried about it. As far as typos go, I don't think that's a new problem (it certainly isn't
I think it's worse than that. I'm using Tim Delaney's code and I'm not really sure why it's doing what it's doing. import sys class Color(Enum): RED, GREEN, BLUE if sys.platform: MAGENTA works and doesn't create Color.sys. import sys class Color(Enum): RED, GREEN, BLUE if sys: MAGENTA works and _does_ create Color.sys. So somehow there's enough magic to realise that if you are accessing an attribute, it's not going to be an enum. In other words, you don't need the ALLCAPS anyway. You might think "oh, good!". I think "oh noes!". It's way too magic for me. The other idea: class Color(Enum): values = "RED", "GREEN", "BLUE", "MAGENTA", "OTHER", "COLORS", "HERE", "TOO" could be written class Color(Enum): values = "RED GREEN BLUE MAGENTA OTHER COLORS HERE TOO".split() so I'm OK with that, but then you're in the territory when you might as well go with the method Barry suggested.

Sorry to anyone who gets this twice - sent it from my work address which is not subscribed to Python-Ideas. On 13 February 2013 06:13, Guido van Rossum <guido@python.org> wrote:
Well, that particular case would work (you'd get a NameError: sys) due to having done an attribute lookup on sys, but the following: class Color(Enum): RED, GREEN, BLUE if platfor == 'win32': MAGENTA would create an enum value 'platfor'. Personally, I don't think it's possible to have an enum with a simple interface and powerful semantics with just python code without it being fragile. I think I've got it fairly close, but there is horrible magic in there (multiple kinds) and there are definitely still edge cases. Any complete enum implementation is going to need some special handling by the parser I think. I'm actually thinking that to simplify things, I need a sentinel object to mark the end of the enum list (which allows other names after it). But that still wouldn't handle the case above (the if statement). BTW, for anyone who hasn't seen the magic code (largely uncommented, no explanations yet of how it's doing it - I probably won't get to that until the weekend) it's here: https://bitbucket.org/magao/enum Tim Delaney

On 13 February 2013 07:13, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
BTW, I've just addressed this in my latest version up on BitBucket. Any usage of an object that is more than just assignment (basically, anything that results in a call of a __dunder__ method on the object) will now mark the name as not an enum value and result in a NameError for an unknown name. Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
Tim Delaney

Ethan Furman wrote:
Or class Color(Enum): values(BLACK, RED, GREEN, BLUE, CYAN, YELLOW, MAGENTA) where values() ignores its arguments and ends the definition phase. This would also provide a way to avoid relying on language guarantees of evaluation order. The initial lookup of the names could assign arbitrary values to them, and then values() could remap them to sequential values based on the order it receives them in. -- Greg

Hello.
masks/flags I see more as a special case of ints (if you use this you are likely trying to match some API defined in C or C++).
Comfortably encoding flags as int is just a half of FlagsEnum functionality. It has also abstract set semantics, see http://mail.python.org/pipermail/python-ideas/2013-January/019150.html . Regards, Drekin

Guido van Rossum <guido@python.org> wrote:
Forgive my naiveness, but why do we need a new type for enums? Wouldn't a new builtin function that returns a dict suffice? Something like: def enum(*keys): return dict((k, i) for i, k in enumerate(keys)) loglevels = enum('debug', 'warning', 'error') -- Markus

Guido van Rossum <guido@python.org> wrote:
Oh, I thought you were just against quoted names if they were also accessible without those:
Whenever I see quoted names that are also used unquoted, I cringe a little (and this includes calls to hasattr() or getattr() with a string literal for the name). -- Markus

On Feb 12, 2013, at 09:07 AM, Ethan Furman wrote:
IMHO, none of these approach the simplicity and explicitness of this API: class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 class FieldType(Enum): BINARY = 1 AUTOINC = 2 NULLABLE = 4 The savings in a few characters, does not (again IMHO) make up for all the magic and implicitness you have to guess at with the top versions. I also don't find much value in the 'unique' style, which seem like they're just another way to name string constants. Here though, you could argue that the DRY principle at least makes them interesting, since IIUC, the explicit alternative would be class Geometry: LEFT = 'left' RIGHT = 'right' TOP = 'top' # ... and so on ... I just wouldn't call these "enums" though. ;) Again, being a little more explicit seems like a win, or at least not so bad. Cheers, -Barry

On 02/12/2013 11:45 AM, Barry Warsaw wrote:
0) Once you read the docs you don't have to guess ;) 1) Magic is fun. :) 2) I hate repetition (= 1 = 2 = 3 is repetitive) 3) I make mistakes when repeating stuff (... = 11 = 11 = 13)
Class membership can also be an advantage. -- ~Ethan~

On Tue, Feb 12, 2013 at 9:58 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
1) Magic is fun. :)
I heartily agree. Check this out:
And here's the dirty trick.... import inspect def enum(): """If you use this your computer will most likely burst ablaze and your teeth will fall off""" frame = inspect.stack()[1][0] # 4 = 3 for the function call "enum()", 1 for the opcode "UNPACK_SEQUENCE", # the next two bytes represent how many things we need to unpack. a, b = frame.f_code.co_code[frame.f_lasti + 4:frame.f_lasti + 6] enum_len = a + b * 256 return range(enum_len) Cheers, Yuval

On 13/02/13 06:45, Barry Warsaw wrote: [...]
My perspective from the peanut gallery is, not so. That "little more" explicitness actually raises the barrier to using enums enough that they are underused. In my experience, good practice or not, too people avoid defining enum-like constants: LEFT = 'left' RIGHT = 'right' ... and just embed the string literals in their code instead. def move(distance, direction): if direction == 'left': ... It's hard to avoid the "magic constant" anti-pattern, and too easy to fall into the bad habit. Speaking as an ex-Pascal programmer, I really miss the simplicity of creating enumerated types. type units = (inches, feet, furlongs, miles); relationship = (parent, sibling, child); which would correspond to Tim's suggestion: class units(Enum): INCHES, FEET, FURLONGS, MILES class relationship(Enum): PARENT, SIBLING, CHILD I would use that, since I don't really care what the values of the enums are. All I care is: - they're unique within a type/class; - they print like their name (e.g. FEET not 1); - (optional) they can combine as bitmasks. Not having to set their value explicitly is a *good thing*. If I don't know the underlying value, I'm not tempted to do this: some_func(42, unit=1) # should be unit=FEET But if I had to use something like this: class units(Enum): INCHES = val() FEET = val() FURLONGS = val() MILES = val() or this: class relationship(Enum): PARENT, SIBLING, CHILD = range(3) I'd probably wouldn't bother. I'd stick to "poor man's enum", like this: PARENT, SIBLING, CHILD = range(3) or (to my shame, but I have to be realistic) magic constants. -- Steven

Frankly, enums are not that useful in small programs. For large programs or libraries, and especially for public APIs, the extra cost of defining the enum shouldn't count against them. Let's just import Barry's enums into the stdlib. -- --Guido van Rossum (python.org/~guido)

On 13 February 2013 09:56, Guido van Rossum <guido@python.org> wrote:
That's entirely your call. FWIW I probably won't use them, as they fail to meet my needs for an enum, #1 being not having to specify their values in any way if I don't want to. Once you get past 3 or 4 values, it's too easy to miss or reuse a value. Tim Delaney

On 13 Feb 2013 09:11, "Tim Delaney" <timothy.c.delaney@gmail.com> wrote:
to meet my needs for an enum, #1 being not having to specify their values in any way if I don't want to. Once you get past 3 or 4 values, it's too easy to miss or reuse a value. What's wrong with enum.make? That just accepts the sequence of names namedtuple style, no values specified anywhere. Cheers, Nick.

On Tue, Feb 12, 2013 at 11:07 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
too find them ugly but not enough to not say this isn't the best solution considering everyone's complaints of verbosity (e.g. Guido not liking excessive quoting), magic (e.g. all non-explicit proposals that don't involve calling a classmethod like val()), and conciseness (e.g. not having to specify every value). Heck, if it was me I would just do:: import types def make_enum(keys, *, value=None): """For incremental numbers: ``lambda key, index: index``. For bitwise flags: ``lambda key, index: 2**index``. Could change to take an iterator instead of a lambda and simply special-case the `None` argument for strings, but that seems needlessly limiting when the integer case is probably for interfacing with some C code. """ if value is None: value = lambda key, index: key items = {} for key, index in enumerate(keys.split()): items[key] = value(key, index) return types.SimpleNamespace(**items) # Creating a read-only subclass is trivial But I know people will want fancier reprs, globally unique objects, etc. instead of just some simple way to create an object with attributes referencing automatically-generated, unique, predictable values (which is all I would want from an enum).

On Wed, Feb 13, 2013 at 7:09 PM, Brett Cannon <brett@python.org> wrote:
Or just a function idiom. We don't have to support the class-based definition style either.
Well, namedtuple has an enum for tuple indexes. I think we should be consistent which entails: Colors = collections.enum('RED GREEN BLUE') Cheers, Yuval

On 14 Feb 2013 02:39, "Antoine Pitrou" <solipsis@pitrou.net> wrote:
Really? What happened to the principle of layered API complexity, where a simple *convenience* function is the obvious way to do it for the cases it covers, while the full, more powerful, but also more verbose, explicit subclassing API covers the more complex cases? Magic namespaces and metaclasses sure don't meet *my* definition of obvious. A normal subclassing API with a convenience function for simple cases? That's just good layered API design. Cheers, Nick.

On Thu, 14 Feb 2013 09:11:05 +1000 Nick Coghlan <ncoghlan@gmail.com> wrote:
Yes, what happened to it? Do you think enums are an important enough feature to warrant a "layered API"? Why stop at 2 layers? And why do you think subclassing is actually a complex API? It's easily understood by any OO programmer out there, even non-Python experts. Why do you think Python ORMs choose a subclassing API instead of your so-called "obvious way to do it using a convenience function"? Hint: nobody would find the convenience function API as obvious as the subclassing API.
Magic namespaces and metaclasses sure don't meet *my* definition of obvious.
Perhaps, but Python is not a supermarket where you choose your own brand of obvious as you like. It's a programming language striving to provide a rather consistent experience. People with bizarre tastes can always publish their packages on PyPI. Regards Antoine.

On Thu, Feb 14, 2013 at 5:35 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Hint: nobody would find the convenience function API as obvious as the subclassing API.
# Status quo one, two, three, four = range(4) # flufl.enum.make -> types.new_enum # Factory function for classes # For when you don't care about the specific values # Close parallel to collections.namedtuple OneToFour = types.new_enum("OneToFour", "one two three four") # flufl.enum.Enum -> types.Enum # For when you do care about (some) specific values class OneToFourAndTen(OneToFour): ten = 10 collections.namedtuple is the normative example here, and while the repetition of the class name is still annoying, that's a minor irritation compared to completely violating everyone's expectations of normal class behaviour.
In which case, they should stop discussing them on python-ideas. flufl.enum is a perfectly fine example of normal Pythonic class design, with similar examples already in the standard library. The approaches being thrown around on this list lately are very cool from a technical point of view, and impressive illustrations of what Python's metaclass machinery can do for you, but they're also completely unintuitive black magic (including "x = ..." having any result other than "x is Ellipsis"). Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Le Thu, 14 Feb 2013 20:40:37 +1000, Nick Coghlan <ncoghlan@gmail.com> a écrit :
I find it slightly amusing that you're complaining about a violation one of your latest PEPs is trying to make easier to make. But, still, you are not convincing me that namedtuple is normative in any way. The fact that many people would prefer a class-based declarative syntax for namedtuple is proof. By the way, not only is the repetition of the class name annoying, but it also makes subclassing more annoying too. Most of my uses of namedtuple imply subclassing, because I add some behaviour (for example additional constructors or serializers). So, compare: _BaseProtocolItem = namedtuple('_BaseProtocolItem', ('serial_no', 'command')) class ProtocolItem(_BaseProtocolItem): # snip custom methods To the hypothetical: class ProtocolItem(NamedTuple): __fields__ = ('serial_no', 'command') # snip custom methods And you understand why the namedtuple() factory function is really an inferior solution with too much typing.
You're still ignoring the less-magic solutions such as: class Errno(Enum): __enumvalues__ = ('EBADF', 'ENOENT',) Regards Antoine.

On Thu, Feb 14, 2013 at 9:16 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Yes, an approach like that, or anything similarly explicit would be fine with me (and has the virtue of potentially supporting pickle, unlike anonymous class generators like the current namedtuple implementation). It was the clever insanity of some of the other ideas being kicked around in these threads that I wanted to be clear I would fight tooth and nail if they were ever actually proposed for stdlib inclusion. My enthusiasm for flufl.enum in particular is mainly of the "I'm tired of seeing almost the exact same enum discussion get rehashed every couple of years, and that one looks good enough to me" variety, so it probably isn't wise to pay too much attention to me when it comes to the specific of the API :P Your comments did give me an idea about namedtuple though - I'll start another thread about that.
I find it slightly amusing that you're complaining about a violation one of your latest PEPs is trying to make easier to make.
[Replying to this second, since it is getting into philosophy of language design, and I actually agree with your key point above] I have a long history of trying to demystify various aspects of Python metaprogramming and make it easier for people to explore the language, and create suitable meta-frameworks for their own use, and for use within particular subcommunities. As I see it, violating normal expectations for class behaviour *within the context of a framework* is fine, *so long as you can't achieve reasonable syntax in a simpler way*. Environments like Django, SQL Alchemy, etc set up new norms about expected behaviour in different kinds of classes, and that's the entire reason I think these metaprograming tools are worth providing in the first place (although I acknowledge it *can* cause problems when people learn one of these frameworks *first*, and then later need to be taught the difference between the automatically provided framework level behaviour for Forms, Models, Views, etc and the underlying standard language behaviour). While the standard library already provides three distinct class level behaviours (normal classes, subclasses of type and ABCs), those are each intertwined deeply with the core type system (arguably, inheriting from builtins other than type or object counts as an additional case, since inheriting from a C defined class that isn't really designed for it has interesting consequences in CPython due to implementation limitations). Furthermore, while our variants change the way instantiation, descriptors, isinstance and issubclass behave after the class is created, they *don't* alter the runtime semantics of the class body itself. To my mind, defining enumerations doesn't even come close to reaching the bar that would justify highly customised class behaviour that a reader can't guess simply from knowing Python and reading the code (I get the impression you already agree with me on this point, which is why I moved this philosophical section after my agreement with you above). Cheers, Nick -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

And now for something completely different: If the Enums are used with attribute syntax, why not this?
Enum.Colors.red.green.blue <Enum: Colors>
This is my 5-minute implementation <https://gist.github.com/jbvsmo/4958012>(quite fun) Looks like everybody is fighting against the class syntax and it is very incompatible with the notion of enum we've got from other languages. BTW, I'm a big fan of flufl.enum João Bernardo 2013/2/14 Nick Coghlan <ncoghlan@gmail.com>

João Bernardo wrote:
Interesting idea, but there seem to be a couple of problems. It looks like you intend Enum to keep a record of all the enum types you create with it. What happens if two unrelated modules both define an enum called Colors? To avoid this problem, you'd want to assign the newly created enum type to a local name, but then you need to write the name twice: Colors = Enum.Colors.red.green.blue or Colors = Enum("Colors").red.green.blue Then what happens if you mistakenly write: print(Colors.pineapple) Seems like this would inadvertently add a new value to the enum instead of producing an AttributeError. -- Greg

This is just an idea to avoid class definition, but it can be improved. 2013/2/15 Greg Ewing <greg.ewing@canterbury.ac.nz>
Enum could write it in the current namespace, just like the `class` statement.
This could be solved with a function call to tell the enum to stop accepting new values.
Each attribute access on `Enum` would create a new class even if it's already defined in the same namespace. The use of the name `Enum` is just good for definition anyway. João Bernardo

On 15 February 2013 03:37, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Don't be picky - it is an interesting idea, and the obvious way to avoid such side effect would be to require: Colors = Enum("Colors").RED.GREEN.BLUE implementation might be tricky this way - you don't want new values genreated each time one fetches a new attribute from a what should be a constant - then maybe: Colors = Enum("Colors").RED.GREEN.BLUE._endEnum
Writing the name of the ENum set twice might be the ideal, but it is what one can do to get away today, in Python - and people are used to it (I'd favor a mechanism for an object to know its name, but that is another issue entirely.)
-- Greg
PS. all - please note João Bernardo and me (João Bueno) are different people ------------------------

On 14/02/13 03:38, Antoine Pitrou wrote:
I disagree. enum.make is the more obvious solution, less verbose and IMO nicer looking too. It's explicit that it makes enums, the API is familiar to anyone who has used namedtuple, and it's an expression rather than a statement so it's more flexible. You can't do this with the class-based syntax: [enum.make(name % i, factory()) for (i, factory) in enumerate(factories)] Besides, the presence of a second, non-obvious solution is not a violation of One Obvious Way. I find it amusing that we as a community put so much emphasis on the Zen which ironically includes one of Tim Peter's subtle jokes. http://bugs.python.org/issue3364 Python is not Perl, but really, there's hardly anything in Python that can't be done two ways if you really try. -- Steven

On Thu, Feb 14, 2013 at 9:48 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Besides, the presence of a second, non-obvious solution is not a violation of One Obvious Way.
Something that is often forgotten is that having two ways to do something is often *good* for your API design, because it lets you design one simple API that covers a large fraction of use cases, and then a more complex underlying API that covers all (or almost all) of the rest. A procedural wrapper around an object-oriented core is the classic means of achieving this, and the standard library does it all over the place (sometimes we don't publish the OO core, but the option is there if our users demand the extra flexibility). The trick to doing it well is to make sure that users are aware that when the simple API is sufficient, that's the one they should use. Only when that API is inadequate should they reach for the more complex one. It's very, very easy to fall into the trap of documenting the comprehensive core first, and then saying "oh, by the way, here's this convenient helper function that means you will probably never need to worry about all that underlying complexity". The subprocess docs used to fall into that trap: call, check_call and check_output cover many use cases, with Popen as the complex fallback, but the old docs hit readers in the face with Popen, and only mentioned the helper functions as an afterthought. The new docs have the helper functions front and centre, with Popen relegated to "if you really need it" status. The flufl.enum docs *do* currently fall into the trap of treating enum.make as an afterthought rather than as the preferred creation API for the cases that it can handle, though. Armin Ronacher has a lovely elaboration of this principle here: http://lucumr.pocoo.org/2013/2/13/moar-classes/ (although he's mostly complaining about the other direction, simple procedural APIs which *don't* expose a flexible OO core, rather than the one here where an OO core is exposed ). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Feb 14, 2013, at 04:18 PM, Nick Coghlan wrote:
I guess that's because .make() *was* an afterthought. ;) .make() was added long after the class syntax. IIRC, Michael Foord suggested that API to allow creation of enums programmatically instead of statically, which the class syntax requires, modulo exec(). I suppose I've never really thought of .make() as a convenience because of that, and because I personally prefer the class syntax, all things being equal. It's interesting that other folks view .make()'s primary benefit as being one of convenience. -Barry

Guido van Rossum writes:
Frankly, enums are not that useful in small programs.
This seems like a very personal thing to me. In some sense that I suppose can be made as objective as counting keystrokes[1], I have to agree with you. But I don't feel that Steven's cringing at "magic literals" is a less valid argument for supporting a particular syntax than your cringing at "quoted identifier names". Of course, technically speaking, Steven could perfectly well use "explicit-valued" enums[2] to eliminate magic literals. But from a beautiful language perspective, ISTM that he is just one among many to advocate that an enum *definition* should look like an *unordered list of identifiers*.[3] It's not about keystroke count, it's about correspondence of syntax to semantics. He's not doing himself any favors by replacing a few magic literals with a hard to read, overdefined enum declaration. I strongly suspect that if you put explicit-valued enums into the stdlib now, you will find that there remains a strong demand for "nice" enums as Tim calls them (although most RFEs will probably use the word "real"!) Footnotes: [1] But far more important, I grant you that. ;-) [2] Note that EIBTI is *not* an argument for explicit-valued enums, because an enum elements's name and class should tell you everything you need to know about its semantics. If it doesn't, you aren't using it as an enum! And "YELLOW = val()" is hardly explicit. :-( [3] If that "list" of names actually has more structure (a set that allows subsetting and union as commonly done with flags, or an order such as the spectrum of colors), it would be nice if that structure is imposed automatically as well -- but this should be done by a subclass such as OrderedEnum or FlagEnum which makes the structure explicit.

On 02/12/2013 02:56 PM, Guido van Rossum wrote:
I have to disagree. I'm working on converting data from program A to program B and it involves many small scripts for the different input files. Each script ranges from 10 - 50 lines long, and enums -- which I use to access the various columns from the incoming file -- are incredibly useful.
Let's just import Barry's enums into the stdlib.
Weren't you at one point unhappy that Barry's enums were not int based? At any rate, non-int based enums would absolutely *not* work for me. for line in open('somefile.txt'): fields = line.split('\t') name = NameCase(fields[int(NAME)]) business = BsnsCase(fields[int(COMPANY)]) street = AddrCase(fields[int(ADDRESS)]) etc., etc., and stab me now with my keyboard! ;) -- ~Ethan~

Am 12.02.2013 20:45, schrieb Barry Warsaw:
I am also against this. It requires special support and heuristics in static analysis tools in order not to flag these as errors. It will be annoying.
We could even allow class Color(Enum): RED = 1 GREEN = ... # becomes 2 BLUE = ... # 3 MAGENTA = 5 FLUFL = ... # 6 class Color(FlagEnum): RED = 1 GREEN = ... # becomes 2 BLUE = ... # 4 MAGENTA = 16 FLUFL = ... class Color(StringEnum): RED = ... # becomes 'red' GREEN = ... # etc. BLUE = ... Georg

On Tue, Feb 12, 2013 at 4:57 PM, Georg Brandl <g.brandl@gmx.net> wrote:
Nice. Both the use of ellipsis and having the semantics depend directly on which specific class you inherit from. At the same time, Steven D'Aprano's proposition of "the underlying value is irrelevant" and "must be bitwise comparable" appeals to me. If the underlying values matter then I'd call it a "constant" rather than an "enum". A proper enum type would remove the temptation to use the underlying value. I would say that there is room for both an enum type and one or more constants type (e.g. what Georg demonstrated above). Regardless, this discussion would be well-served by recognizing the distinction. -eric

On Tue, Feb 12, 2013 at 6:07 PM, Eric Snow <ericsnowcurrently@gmail.com> wrote:
Here's an implementation of what I'm talking about: http://code.activestate.com/recipes/578455/ Each enum value is simply an object and not backed by any underlying value. The bitwise operators are implemented. The implementation is a little rough, but hopefully gets the idea across. -eric

Am 13.02.2013 08:21, schrieb Antoine Pitrou:
Well, nobody forbids you from writing the integers out. But one of the complaints was that you shouldn't need to know or write the constant values (and "..." is shorter than "val()" which was proposed for that purpose). And for stringy enums that argument is void :) Georg

On Wed, 13 Feb 2013 08:34:21 +0100 Georg Brandl <g.brandl@gmx.net> wrote:
It would still be shorter to type something like: class Color(Enum): values = ('RED', 'GREEN', 'BLUE', 'MAGENTA', 'FLUFL')
And for stringy enums that argument is void :)
I'm not sure how it's void. What is the proposed syntax for string enums? Regards Antoine.

On Feb 13, 2013, at 12:57 AM, Georg Brandl wrote:
That's pretty cute.
I want to think about the latter two (especially the string one), but I've filed this bug to track the feature. https://bugs.launchpad.net/flufl.enum/+bug/1124357 Cheers, -Barry

13.02.2013 00:57, Georg Brandl wrote:
It's nice. But what about synonymous items? class Color(Enum): RED = R = 1 GREEN = ... BLUE = B = ... # we ment BLUE = B = 3, but effectively # we'll have BLUE = 3 and B = 4 :-| What about: (all comments are for explanation only) class Color(Enum): RED = 1 # 1 GREEN = +one # 2 BLUE = +one # 3 YELLOW = Y = +one # 4 BLACK = B = 10 # 10 ORANGE = -one # 9 VIOLET = -one # 8 class Flag(Enum): FOO = 1 # 1 BAR = +rot # 2 BAZ = +rot # 4 BOO = +rot # 8 SPAM = 256 # 256 HAM = -rot # 128 RAM = -rot # 64 class Color(Enum): RED = ... # 'RED' GREEN = ... # 'GREEN' BLUE = ... # 'BLUE' and maybe also: class Color(Enum): # 0 1 2 3 RED, GREEN, BLUE, YELLOW, *end = seq() class Color(Enum): # 3 4 5 6 RED, GREEN, BLUE, YELLOW, *end = seq(3) class Flag(Enum): # 1 2 4 8 16 FOO, BAR, BAZ, BOO, SPAM, *end = flags() (yes, it *is* possible to implement it without playing with stack frames...) Cheers. *j PS. And now for something completely different: :-) @enum def Color(v): v.RED # 0 v.GREEN # 1 v.BLUE # 2 v.YELLOW # 3 @enum def Color(v): v.RED = 7 v.GREEN # 8 v.BLUE # 9 v.YELLOW # 10

On 02/13/2013 05:58 PM, Ryan Hiebert wrote:
Already is, since 3.0 I believe. http://www.python.org/dev/peps/pep-3132/ -- ~Ethan~

On 12 February 2013 13:31, Antoine Pitrou <solipsis@pitrou.net> wrote:
That is too much ' ' typing - I think it would be ok, to have it like that, but a helper function that would work just like the namedtuple call: Color = Enum("Color", "RED GREEN BLUE", int) The magic of class Color(Enum): RED, GREEN, BLUE is tempting, but basically, what is required so that it does not break class Color(Enum): RED = other_module.value (without making "other_module" a new enum value :-) ) if not worth it, IMHO. such magic is implemented in Tim's proof of concept. A midle term could be to allow declarations like class Color(IntEnum): RED, BLUE, GREEN but with no other logic or expressions in the class body -- all names showing up are strictly part of the sequence, and them have a constructor call, with keyword arguments for all the other cases.
+1 One other important point, that is missed somewhere along Tim's implementation: the repr and str of the enum values should be the value's name - no need to the fancy representation. Actually, the fancy representation defeats most of the purpose of having the constants/enums They could have a property like Enum._qualified_name if one needs it.
Regards
Antoine.

On 12 February 2013 17:50, Barry Warsaw <barry@python.org> wrote:
Not bad - I think that wahtever is agreeded in a nice api that takes advantage of the "class" statement we should have a callabale constructor that takes names as strings. And moreover, it is nice to have a way to load the created enums/constants to the current namespace - and it can be done, without "magic" to the global namespace having a call that takes a "namespace" argument to which one would normally pass in blobals() So: from enum import load, make would allow for:
(I think that allowing a call without the "split()" would be better - ) That would make some *linters complain - but those should update themselves to the fact in time. I consider a way to load the created constants into the module namespace very important to the concept of enums/constant itself. Agreeing on a call that can get "globals()" and update it would preclude the idea of being able to do "from MyEnumClass import *" that showed up on the previous thread. js -><-

On Feb 13, 2013, at 10:08 AM, Steven D'Aprano wrote:
Part of the reason for defining make() the way it is, is so that you can use any iterator in the second argument. I suppose you could special case splitting if the second argument is a single string, but I'm not sure this special case is special enough. -Barry

On Feb 12, 2013, at 04:31 PM, Antoine Pitrou wrote:
(which allows you to build enums programmatically too)
I prefer an alternative API for creating enums programmatically, e.g.: http://pythonhosted.org/flufl.enum/docs/using.html#alternative-api -Barry

Le Tue, 12 Feb 2013 07:21:04 -0800, Eli Bendersky <eliben@gmail.com> a écrit :
I think I'd favour a namedtuple-style approach, e.g.: class Color(Enum): fields = ('RED', 'BLUE', 'GREEN') (which allows you to build enums programmatically too) Also, I think you have to provide several classes, at least SequenceEnum and StringEnum, and perhaps also BitmaskEnum. It makes sense for them to be separate types, 1) because it makes it explicit how the given enum behaves, 2) because combining integer and str constants in an enum would be crazy. Regards Antoine.

On 02/12/2013 07:31 AM, Antoine Pitrou wrote:
I partially agree with Antoine; there should be three types of Enum available: - sequence (most common: 0, 1, 2, 3, etc.) - flag (1, 2, 4, 8, etc.) - unique ('on', 'off'; 'north', 'south', 'east', 'west') and they shouldn't be mixed together higgledy-piggledy in the same class. However, I like the simplicity of Tim's approach. The result of the implementation I've been playing with looks something like: class Color(Enum): type = 'sequence' RED, GREEN, BLUE class Geometry(Enum): type = 'unique' LEFT, RIGHT, TOP, BOTTOM WEST, EAST, NORTH, SOUTH class FieldType(Enum): type = 'flag' BINARY, AUTOINC, NULLABLE The metaclass takes care of assigning the next value and selecting the proper subclass; 'unique' values are lower-cased (LEFT -> 'left'); subclassing is possible; and each Enum value is properly an instance of it's Enum class. One important reason for having the different types of Enum is for prototyping. -- ~Ethan~

I'm torn. I like the clean look of Tim's: class Flag(Enum): RED, WHITE, BLUE However, I don't like the magic needed for its implementation -- and anybody who thinks they know Python pretty well and thinks about this syntax for more than two seconds has to wonder how on earth it's done (and the answer is pretty horrible). It is also pretty brittle to depend on the lookup order -- and I suspect it will be prone to masking other bugs (any typo in a name used in class-level code will essentially generate a bogus new enum value). OTOH I don't like having the string quotes of Antoine's counter-proposal: class Flag(Enum): fields = ('RED', 'WHITE', 'BLUE') Whenever I see quoted names that are also used unquoted, I cringe a little (and this includes calls to hasattr() or getattr() with a string literal for the name). The compromise that I would personally lean towards is more like this: class Flag(Enum): RED, WHITE, BLUE = <something> Even if the <something> requires you to know how many values you are defining, like range(3). If we have to have a somewhat more verbose syntax that doesn't bother me too much. FWIW I do like being able to define enums that are ints and strings (but not mixed); masks/flags I see more as a special case of ints (if you use this you are likely trying to match some API defined in C or C++). I also agree that it must be possible to customize the enum class and the behavior of the values by defining methods on the class. -- --Guido van Rossum (python.org/~guido)

On 02/12/2013 10:08 AM, Guido van Rossum wrote:
It seems to me the point of an enum is to give names to an order, so why would the lookup order be a problem?
Having just dealt with using a poor-person's Enum (THIS, THAT, THE_OTHER = range(3)) and then having to update it a few times (including all the places in the code that depended on the length), I can safely say that the range(n) construct is a PITA. If you don't mind verbose, we could always go for something like: class Color(Enum): start_defs() BLACK RED, GREEN, BLUE CYAN, YELLOW, MAGENTA end_defs() and if we mandate that the enum names must come first we can even ditch the 'start_defs' call. As far as typos go, I don't think that's a new problem (it certainly isn't for me, anyway ;) and my best defense is plenty of tests. -- ~Ethan~

On Tue, Feb 12, 2013 at 10:31 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Because the language requirement that the expressions in a tuple are evaluated left-to-right is fairly weak. (There are other, similar contexts where the order is not l2r.)
If that's where you're leaning I would much rather do this: class Color(Enum): BLACK = val() RED = val() # etc. EIBTI.
As far as typos go, I don't think that's a new problem (it certainly isn't for me, anyway ;) and my best defense is plenty of tests.
So with Tim's implementation, what happens here: class Color(Enum): RED, GREEN, BLUE if sys.platform == 'win32': MAGENTA and you happen to have no "import sys" in your module? The sys lookup will succeed, create a new enum, and then you will get an error something like this: AttributeError: 'Enum' object has no attribute 'platform' -- --Guido van Rossum (python.org/~guido)

On 02/12/2013 11:13 AM, Guido van Rossum wrote:
Ah.
Personally, I'd be fine with: class Color(Enum): BLACK RED BLUE PURPLE which avoids the l2r issue, and the repetitive use of val() (or whatever it's called).
We could minimize that issue by requiring that enum names be ALLCAPS -- if something comes through that isn't, don't supply a value and let the AttributeError perculate up. -- ~Ethan~

On 12 February 2013 19:26, Ethan Furman <ethan@stoneleaf.us> wrote:
Can you force people not to use: class Color(Enum): BLACK, RED, BLUE, PURPLE ? If I'm understanding the implementation, if you support one then you support the other. Hence the problem will still stand. That said, I'm not personally too worried about it. As far as typos go, I don't think that's a new problem (it certainly isn't
I think it's worse than that. I'm using Tim Delaney's code and I'm not really sure why it's doing what it's doing. import sys class Color(Enum): RED, GREEN, BLUE if sys.platform: MAGENTA works and doesn't create Color.sys. import sys class Color(Enum): RED, GREEN, BLUE if sys: MAGENTA works and _does_ create Color.sys. So somehow there's enough magic to realise that if you are accessing an attribute, it's not going to be an enum. In other words, you don't need the ALLCAPS anyway. You might think "oh, good!". I think "oh noes!". It's way too magic for me. The other idea: class Color(Enum): values = "RED", "GREEN", "BLUE", "MAGENTA", "OTHER", "COLORS", "HERE", "TOO" could be written class Color(Enum): values = "RED GREEN BLUE MAGENTA OTHER COLORS HERE TOO".split() so I'm OK with that, but then you're in the territory when you might as well go with the method Barry suggested.

Sorry to anyone who gets this twice - sent it from my work address which is not subscribed to Python-Ideas. On 13 February 2013 06:13, Guido van Rossum <guido@python.org> wrote:
Well, that particular case would work (you'd get a NameError: sys) due to having done an attribute lookup on sys, but the following: class Color(Enum): RED, GREEN, BLUE if platfor == 'win32': MAGENTA would create an enum value 'platfor'. Personally, I don't think it's possible to have an enum with a simple interface and powerful semantics with just python code without it being fragile. I think I've got it fairly close, but there is horrible magic in there (multiple kinds) and there are definitely still edge cases. Any complete enum implementation is going to need some special handling by the parser I think. I'm actually thinking that to simplify things, I need a sentinel object to mark the end of the enum list (which allows other names after it). But that still wouldn't handle the case above (the if statement). BTW, for anyone who hasn't seen the magic code (largely uncommented, no explanations yet of how it's doing it - I probably won't get to that until the weekend) it's here: https://bitbucket.org/magao/enum Tim Delaney

On 13 February 2013 07:13, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
BTW, I've just addressed this in my latest version up on BitBucket. Any usage of an object that is more than just assignment (basically, anything that results in a call of a __dunder__ method on the object) will now mark the name as not an enum value and result in a NameError for an unknown name. Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.
Tim Delaney

Ethan Furman wrote:
Or class Color(Enum): values(BLACK, RED, GREEN, BLUE, CYAN, YELLOW, MAGENTA) where values() ignores its arguments and ends the definition phase. This would also provide a way to avoid relying on language guarantees of evaluation order. The initial lookup of the names could assign arbitrary values to them, and then values() could remap them to sequential values based on the order it receives them in. -- Greg

Hello.
masks/flags I see more as a special case of ints (if you use this you are likely trying to match some API defined in C or C++).
Comfortably encoding flags as int is just a half of FlagsEnum functionality. It has also abstract set semantics, see http://mail.python.org/pipermail/python-ideas/2013-January/019150.html . Regards, Drekin

Guido van Rossum <guido@python.org> wrote:
Forgive my naiveness, but why do we need a new type for enums? Wouldn't a new builtin function that returns a dict suffice? Something like: def enum(*keys): return dict((k, i) for i, k in enumerate(keys)) loglevels = enum('debug', 'warning', 'error') -- Markus

Guido van Rossum <guido@python.org> wrote:
Oh, I thought you were just against quoted names if they were also accessible without those:
Whenever I see quoted names that are also used unquoted, I cringe a little (and this includes calls to hasattr() or getattr() with a string literal for the name). -- Markus

On Feb 12, 2013, at 09:07 AM, Ethan Furman wrote:
IMHO, none of these approach the simplicity and explicitness of this API: class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 class FieldType(Enum): BINARY = 1 AUTOINC = 2 NULLABLE = 4 The savings in a few characters, does not (again IMHO) make up for all the magic and implicitness you have to guess at with the top versions. I also don't find much value in the 'unique' style, which seem like they're just another way to name string constants. Here though, you could argue that the DRY principle at least makes them interesting, since IIUC, the explicit alternative would be class Geometry: LEFT = 'left' RIGHT = 'right' TOP = 'top' # ... and so on ... I just wouldn't call these "enums" though. ;) Again, being a little more explicit seems like a win, or at least not so bad. Cheers, -Barry

On 02/12/2013 11:45 AM, Barry Warsaw wrote:
0) Once you read the docs you don't have to guess ;) 1) Magic is fun. :) 2) I hate repetition (= 1 = 2 = 3 is repetitive) 3) I make mistakes when repeating stuff (... = 11 = 11 = 13)
Class membership can also be an advantage. -- ~Ethan~

On Tue, Feb 12, 2013 at 9:58 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
1) Magic is fun. :)
I heartily agree. Check this out:
And here's the dirty trick.... import inspect def enum(): """If you use this your computer will most likely burst ablaze and your teeth will fall off""" frame = inspect.stack()[1][0] # 4 = 3 for the function call "enum()", 1 for the opcode "UNPACK_SEQUENCE", # the next two bytes represent how many things we need to unpack. a, b = frame.f_code.co_code[frame.f_lasti + 4:frame.f_lasti + 6] enum_len = a + b * 256 return range(enum_len) Cheers, Yuval

On 13/02/13 06:45, Barry Warsaw wrote: [...]
My perspective from the peanut gallery is, not so. That "little more" explicitness actually raises the barrier to using enums enough that they are underused. In my experience, good practice or not, too people avoid defining enum-like constants: LEFT = 'left' RIGHT = 'right' ... and just embed the string literals in their code instead. def move(distance, direction): if direction == 'left': ... It's hard to avoid the "magic constant" anti-pattern, and too easy to fall into the bad habit. Speaking as an ex-Pascal programmer, I really miss the simplicity of creating enumerated types. type units = (inches, feet, furlongs, miles); relationship = (parent, sibling, child); which would correspond to Tim's suggestion: class units(Enum): INCHES, FEET, FURLONGS, MILES class relationship(Enum): PARENT, SIBLING, CHILD I would use that, since I don't really care what the values of the enums are. All I care is: - they're unique within a type/class; - they print like their name (e.g. FEET not 1); - (optional) they can combine as bitmasks. Not having to set their value explicitly is a *good thing*. If I don't know the underlying value, I'm not tempted to do this: some_func(42, unit=1) # should be unit=FEET But if I had to use something like this: class units(Enum): INCHES = val() FEET = val() FURLONGS = val() MILES = val() or this: class relationship(Enum): PARENT, SIBLING, CHILD = range(3) I'd probably wouldn't bother. I'd stick to "poor man's enum", like this: PARENT, SIBLING, CHILD = range(3) or (to my shame, but I have to be realistic) magic constants. -- Steven

Frankly, enums are not that useful in small programs. For large programs or libraries, and especially for public APIs, the extra cost of defining the enum shouldn't count against them. Let's just import Barry's enums into the stdlib. -- --Guido van Rossum (python.org/~guido)

On 13 February 2013 09:56, Guido van Rossum <guido@python.org> wrote:
That's entirely your call. FWIW I probably won't use them, as they fail to meet my needs for an enum, #1 being not having to specify their values in any way if I don't want to. Once you get past 3 or 4 values, it's too easy to miss or reuse a value. Tim Delaney

On 13 Feb 2013 09:11, "Tim Delaney" <timothy.c.delaney@gmail.com> wrote:
to meet my needs for an enum, #1 being not having to specify their values in any way if I don't want to. Once you get past 3 or 4 values, it's too easy to miss or reuse a value. What's wrong with enum.make? That just accepts the sequence of names namedtuple style, no values specified anywhere. Cheers, Nick.

On Tue, Feb 12, 2013 at 11:07 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
too find them ugly but not enough to not say this isn't the best solution considering everyone's complaints of verbosity (e.g. Guido not liking excessive quoting), magic (e.g. all non-explicit proposals that don't involve calling a classmethod like val()), and conciseness (e.g. not having to specify every value). Heck, if it was me I would just do:: import types def make_enum(keys, *, value=None): """For incremental numbers: ``lambda key, index: index``. For bitwise flags: ``lambda key, index: 2**index``. Could change to take an iterator instead of a lambda and simply special-case the `None` argument for strings, but that seems needlessly limiting when the integer case is probably for interfacing with some C code. """ if value is None: value = lambda key, index: key items = {} for key, index in enumerate(keys.split()): items[key] = value(key, index) return types.SimpleNamespace(**items) # Creating a read-only subclass is trivial But I know people will want fancier reprs, globally unique objects, etc. instead of just some simple way to create an object with attributes referencing automatically-generated, unique, predictable values (which is all I would want from an enum).

On Wed, Feb 13, 2013 at 7:09 PM, Brett Cannon <brett@python.org> wrote:
Or just a function idiom. We don't have to support the class-based definition style either.
Well, namedtuple has an enum for tuple indexes. I think we should be consistent which entails: Colors = collections.enum('RED GREEN BLUE') Cheers, Yuval

On 14 Feb 2013 02:39, "Antoine Pitrou" <solipsis@pitrou.net> wrote:
Really? What happened to the principle of layered API complexity, where a simple *convenience* function is the obvious way to do it for the cases it covers, while the full, more powerful, but also more verbose, explicit subclassing API covers the more complex cases? Magic namespaces and metaclasses sure don't meet *my* definition of obvious. A normal subclassing API with a convenience function for simple cases? That's just good layered API design. Cheers, Nick.

On Thu, 14 Feb 2013 09:11:05 +1000 Nick Coghlan <ncoghlan@gmail.com> wrote:
Yes, what happened to it? Do you think enums are an important enough feature to warrant a "layered API"? Why stop at 2 layers? And why do you think subclassing is actually a complex API? It's easily understood by any OO programmer out there, even non-Python experts. Why do you think Python ORMs choose a subclassing API instead of your so-called "obvious way to do it using a convenience function"? Hint: nobody would find the convenience function API as obvious as the subclassing API.
Magic namespaces and metaclasses sure don't meet *my* definition of obvious.
Perhaps, but Python is not a supermarket where you choose your own brand of obvious as you like. It's a programming language striving to provide a rather consistent experience. People with bizarre tastes can always publish their packages on PyPI. Regards Antoine.

On Thu, Feb 14, 2013 at 5:35 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Hint: nobody would find the convenience function API as obvious as the subclassing API.
# Status quo one, two, three, four = range(4) # flufl.enum.make -> types.new_enum # Factory function for classes # For when you don't care about the specific values # Close parallel to collections.namedtuple OneToFour = types.new_enum("OneToFour", "one two three four") # flufl.enum.Enum -> types.Enum # For when you do care about (some) specific values class OneToFourAndTen(OneToFour): ten = 10 collections.namedtuple is the normative example here, and while the repetition of the class name is still annoying, that's a minor irritation compared to completely violating everyone's expectations of normal class behaviour.
In which case, they should stop discussing them on python-ideas. flufl.enum is a perfectly fine example of normal Pythonic class design, with similar examples already in the standard library. The approaches being thrown around on this list lately are very cool from a technical point of view, and impressive illustrations of what Python's metaclass machinery can do for you, but they're also completely unintuitive black magic (including "x = ..." having any result other than "x is Ellipsis"). Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Le Thu, 14 Feb 2013 20:40:37 +1000, Nick Coghlan <ncoghlan@gmail.com> a écrit :
I find it slightly amusing that you're complaining about a violation one of your latest PEPs is trying to make easier to make. But, still, you are not convincing me that namedtuple is normative in any way. The fact that many people would prefer a class-based declarative syntax for namedtuple is proof. By the way, not only is the repetition of the class name annoying, but it also makes subclassing more annoying too. Most of my uses of namedtuple imply subclassing, because I add some behaviour (for example additional constructors or serializers). So, compare: _BaseProtocolItem = namedtuple('_BaseProtocolItem', ('serial_no', 'command')) class ProtocolItem(_BaseProtocolItem): # snip custom methods To the hypothetical: class ProtocolItem(NamedTuple): __fields__ = ('serial_no', 'command') # snip custom methods And you understand why the namedtuple() factory function is really an inferior solution with too much typing.
You're still ignoring the less-magic solutions such as: class Errno(Enum): __enumvalues__ = ('EBADF', 'ENOENT',) Regards Antoine.

On Thu, Feb 14, 2013 at 9:16 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Yes, an approach like that, or anything similarly explicit would be fine with me (and has the virtue of potentially supporting pickle, unlike anonymous class generators like the current namedtuple implementation). It was the clever insanity of some of the other ideas being kicked around in these threads that I wanted to be clear I would fight tooth and nail if they were ever actually proposed for stdlib inclusion. My enthusiasm for flufl.enum in particular is mainly of the "I'm tired of seeing almost the exact same enum discussion get rehashed every couple of years, and that one looks good enough to me" variety, so it probably isn't wise to pay too much attention to me when it comes to the specific of the API :P Your comments did give me an idea about namedtuple though - I'll start another thread about that.
I find it slightly amusing that you're complaining about a violation one of your latest PEPs is trying to make easier to make.
[Replying to this second, since it is getting into philosophy of language design, and I actually agree with your key point above] I have a long history of trying to demystify various aspects of Python metaprogramming and make it easier for people to explore the language, and create suitable meta-frameworks for their own use, and for use within particular subcommunities. As I see it, violating normal expectations for class behaviour *within the context of a framework* is fine, *so long as you can't achieve reasonable syntax in a simpler way*. Environments like Django, SQL Alchemy, etc set up new norms about expected behaviour in different kinds of classes, and that's the entire reason I think these metaprograming tools are worth providing in the first place (although I acknowledge it *can* cause problems when people learn one of these frameworks *first*, and then later need to be taught the difference between the automatically provided framework level behaviour for Forms, Models, Views, etc and the underlying standard language behaviour). While the standard library already provides three distinct class level behaviours (normal classes, subclasses of type and ABCs), those are each intertwined deeply with the core type system (arguably, inheriting from builtins other than type or object counts as an additional case, since inheriting from a C defined class that isn't really designed for it has interesting consequences in CPython due to implementation limitations). Furthermore, while our variants change the way instantiation, descriptors, isinstance and issubclass behave after the class is created, they *don't* alter the runtime semantics of the class body itself. To my mind, defining enumerations doesn't even come close to reaching the bar that would justify highly customised class behaviour that a reader can't guess simply from knowing Python and reading the code (I get the impression you already agree with me on this point, which is why I moved this philosophical section after my agreement with you above). Cheers, Nick -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

And now for something completely different: If the Enums are used with attribute syntax, why not this?
Enum.Colors.red.green.blue <Enum: Colors>
This is my 5-minute implementation <https://gist.github.com/jbvsmo/4958012>(quite fun) Looks like everybody is fighting against the class syntax and it is very incompatible with the notion of enum we've got from other languages. BTW, I'm a big fan of flufl.enum João Bernardo 2013/2/14 Nick Coghlan <ncoghlan@gmail.com>

João Bernardo wrote:
Interesting idea, but there seem to be a couple of problems. It looks like you intend Enum to keep a record of all the enum types you create with it. What happens if two unrelated modules both define an enum called Colors? To avoid this problem, you'd want to assign the newly created enum type to a local name, but then you need to write the name twice: Colors = Enum.Colors.red.green.blue or Colors = Enum("Colors").red.green.blue Then what happens if you mistakenly write: print(Colors.pineapple) Seems like this would inadvertently add a new value to the enum instead of producing an AttributeError. -- Greg

This is just an idea to avoid class definition, but it can be improved. 2013/2/15 Greg Ewing <greg.ewing@canterbury.ac.nz>
Enum could write it in the current namespace, just like the `class` statement.
This could be solved with a function call to tell the enum to stop accepting new values.
Each attribute access on `Enum` would create a new class even if it's already defined in the same namespace. The use of the name `Enum` is just good for definition anyway. João Bernardo

On 15 February 2013 03:37, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Don't be picky - it is an interesting idea, and the obvious way to avoid such side effect would be to require: Colors = Enum("Colors").RED.GREEN.BLUE implementation might be tricky this way - you don't want new values genreated each time one fetches a new attribute from a what should be a constant - then maybe: Colors = Enum("Colors").RED.GREEN.BLUE._endEnum
Writing the name of the ENum set twice might be the ideal, but it is what one can do to get away today, in Python - and people are used to it (I'd favor a mechanism for an object to know its name, but that is another issue entirely.)
-- Greg
PS. all - please note João Bernardo and me (João Bueno) are different people ------------------------

On 14/02/13 03:38, Antoine Pitrou wrote:
I disagree. enum.make is the more obvious solution, less verbose and IMO nicer looking too. It's explicit that it makes enums, the API is familiar to anyone who has used namedtuple, and it's an expression rather than a statement so it's more flexible. You can't do this with the class-based syntax: [enum.make(name % i, factory()) for (i, factory) in enumerate(factories)] Besides, the presence of a second, non-obvious solution is not a violation of One Obvious Way. I find it amusing that we as a community put so much emphasis on the Zen which ironically includes one of Tim Peter's subtle jokes. http://bugs.python.org/issue3364 Python is not Perl, but really, there's hardly anything in Python that can't be done two ways if you really try. -- Steven

On Thu, Feb 14, 2013 at 9:48 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Besides, the presence of a second, non-obvious solution is not a violation of One Obvious Way.
Something that is often forgotten is that having two ways to do something is often *good* for your API design, because it lets you design one simple API that covers a large fraction of use cases, and then a more complex underlying API that covers all (or almost all) of the rest. A procedural wrapper around an object-oriented core is the classic means of achieving this, and the standard library does it all over the place (sometimes we don't publish the OO core, but the option is there if our users demand the extra flexibility). The trick to doing it well is to make sure that users are aware that when the simple API is sufficient, that's the one they should use. Only when that API is inadequate should they reach for the more complex one. It's very, very easy to fall into the trap of documenting the comprehensive core first, and then saying "oh, by the way, here's this convenient helper function that means you will probably never need to worry about all that underlying complexity". The subprocess docs used to fall into that trap: call, check_call and check_output cover many use cases, with Popen as the complex fallback, but the old docs hit readers in the face with Popen, and only mentioned the helper functions as an afterthought. The new docs have the helper functions front and centre, with Popen relegated to "if you really need it" status. The flufl.enum docs *do* currently fall into the trap of treating enum.make as an afterthought rather than as the preferred creation API for the cases that it can handle, though. Armin Ronacher has a lovely elaboration of this principle here: http://lucumr.pocoo.org/2013/2/13/moar-classes/ (although he's mostly complaining about the other direction, simple procedural APIs which *don't* expose a flexible OO core, rather than the one here where an OO core is exposed ). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Feb 14, 2013, at 04:18 PM, Nick Coghlan wrote:
I guess that's because .make() *was* an afterthought. ;) .make() was added long after the class syntax. IIRC, Michael Foord suggested that API to allow creation of enums programmatically instead of statically, which the class syntax requires, modulo exec(). I suppose I've never really thought of .make() as a convenience because of that, and because I personally prefer the class syntax, all things being equal. It's interesting that other folks view .make()'s primary benefit as being one of convenience. -Barry

Guido van Rossum writes:
Frankly, enums are not that useful in small programs.
This seems like a very personal thing to me. In some sense that I suppose can be made as objective as counting keystrokes[1], I have to agree with you. But I don't feel that Steven's cringing at "magic literals" is a less valid argument for supporting a particular syntax than your cringing at "quoted identifier names". Of course, technically speaking, Steven could perfectly well use "explicit-valued" enums[2] to eliminate magic literals. But from a beautiful language perspective, ISTM that he is just one among many to advocate that an enum *definition* should look like an *unordered list of identifiers*.[3] It's not about keystroke count, it's about correspondence of syntax to semantics. He's not doing himself any favors by replacing a few magic literals with a hard to read, overdefined enum declaration. I strongly suspect that if you put explicit-valued enums into the stdlib now, you will find that there remains a strong demand for "nice" enums as Tim calls them (although most RFEs will probably use the word "real"!) Footnotes: [1] But far more important, I grant you that. ;-) [2] Note that EIBTI is *not* an argument for explicit-valued enums, because an enum elements's name and class should tell you everything you need to know about its semantics. If it doesn't, you aren't using it as an enum! And "YELLOW = val()" is hardly explicit. :-( [3] If that "list" of names actually has more structure (a set that allows subsetting and union as commonly done with flags, or an order such as the spectrum of colors), it would be nice if that structure is imposed automatically as well -- but this should be done by a subclass such as OrderedEnum or FlagEnum which makes the structure explicit.

On 02/12/2013 02:56 PM, Guido van Rossum wrote:
I have to disagree. I'm working on converting data from program A to program B and it involves many small scripts for the different input files. Each script ranges from 10 - 50 lines long, and enums -- which I use to access the various columns from the incoming file -- are incredibly useful.
Let's just import Barry's enums into the stdlib.
Weren't you at one point unhappy that Barry's enums were not int based? At any rate, non-int based enums would absolutely *not* work for me. for line in open('somefile.txt'): fields = line.split('\t') name = NameCase(fields[int(NAME)]) business = BsnsCase(fields[int(COMPANY)]) street = AddrCase(fields[int(ADDRESS)]) etc., etc., and stab me now with my keyboard! ;) -- ~Ethan~

Am 12.02.2013 20:45, schrieb Barry Warsaw:
I am also against this. It requires special support and heuristics in static analysis tools in order not to flag these as errors. It will be annoying.
We could even allow class Color(Enum): RED = 1 GREEN = ... # becomes 2 BLUE = ... # 3 MAGENTA = 5 FLUFL = ... # 6 class Color(FlagEnum): RED = 1 GREEN = ... # becomes 2 BLUE = ... # 4 MAGENTA = 16 FLUFL = ... class Color(StringEnum): RED = ... # becomes 'red' GREEN = ... # etc. BLUE = ... Georg

On Tue, Feb 12, 2013 at 4:57 PM, Georg Brandl <g.brandl@gmx.net> wrote:
Nice. Both the use of ellipsis and having the semantics depend directly on which specific class you inherit from. At the same time, Steven D'Aprano's proposition of "the underlying value is irrelevant" and "must be bitwise comparable" appeals to me. If the underlying values matter then I'd call it a "constant" rather than an "enum". A proper enum type would remove the temptation to use the underlying value. I would say that there is room for both an enum type and one or more constants type (e.g. what Georg demonstrated above). Regardless, this discussion would be well-served by recognizing the distinction. -eric

On Tue, Feb 12, 2013 at 6:07 PM, Eric Snow <ericsnowcurrently@gmail.com> wrote:
Here's an implementation of what I'm talking about: http://code.activestate.com/recipes/578455/ Each enum value is simply an object and not backed by any underlying value. The bitwise operators are implemented. The implementation is a little rough, but hopefully gets the idea across. -eric

Am 13.02.2013 08:21, schrieb Antoine Pitrou:
Well, nobody forbids you from writing the integers out. But one of the complaints was that you shouldn't need to know or write the constant values (and "..." is shorter than "val()" which was proposed for that purpose). And for stringy enums that argument is void :) Georg

On Wed, 13 Feb 2013 08:34:21 +0100 Georg Brandl <g.brandl@gmx.net> wrote:
It would still be shorter to type something like: class Color(Enum): values = ('RED', 'GREEN', 'BLUE', 'MAGENTA', 'FLUFL')
And for stringy enums that argument is void :)
I'm not sure how it's void. What is the proposed syntax for string enums? Regards Antoine.

On Feb 13, 2013, at 12:57 AM, Georg Brandl wrote:
That's pretty cute.
I want to think about the latter two (especially the string one), but I've filed this bug to track the feature. https://bugs.launchpad.net/flufl.enum/+bug/1124357 Cheers, -Barry

13.02.2013 00:57, Georg Brandl wrote:
It's nice. But what about synonymous items? class Color(Enum): RED = R = 1 GREEN = ... BLUE = B = ... # we ment BLUE = B = 3, but effectively # we'll have BLUE = 3 and B = 4 :-| What about: (all comments are for explanation only) class Color(Enum): RED = 1 # 1 GREEN = +one # 2 BLUE = +one # 3 YELLOW = Y = +one # 4 BLACK = B = 10 # 10 ORANGE = -one # 9 VIOLET = -one # 8 class Flag(Enum): FOO = 1 # 1 BAR = +rot # 2 BAZ = +rot # 4 BOO = +rot # 8 SPAM = 256 # 256 HAM = -rot # 128 RAM = -rot # 64 class Color(Enum): RED = ... # 'RED' GREEN = ... # 'GREEN' BLUE = ... # 'BLUE' and maybe also: class Color(Enum): # 0 1 2 3 RED, GREEN, BLUE, YELLOW, *end = seq() class Color(Enum): # 3 4 5 6 RED, GREEN, BLUE, YELLOW, *end = seq(3) class Flag(Enum): # 1 2 4 8 16 FOO, BAR, BAZ, BOO, SPAM, *end = flags() (yes, it *is* possible to implement it without playing with stack frames...) Cheers. *j PS. And now for something completely different: :-) @enum def Color(v): v.RED # 0 v.GREEN # 1 v.BLUE # 2 v.YELLOW # 3 @enum def Color(v): v.RED = 7 v.GREEN # 8 v.BLUE # 9 v.YELLOW # 10

On 02/13/2013 05:58 PM, Ryan Hiebert wrote:
Already is, since 3.0 I believe. http://www.python.org/dev/peps/pep-3132/ -- ~Ethan~

On 12 February 2013 13:31, Antoine Pitrou <solipsis@pitrou.net> wrote:
That is too much ' ' typing - I think it would be ok, to have it like that, but a helper function that would work just like the namedtuple call: Color = Enum("Color", "RED GREEN BLUE", int) The magic of class Color(Enum): RED, GREEN, BLUE is tempting, but basically, what is required so that it does not break class Color(Enum): RED = other_module.value (without making "other_module" a new enum value :-) ) if not worth it, IMHO. such magic is implemented in Tim's proof of concept. A midle term could be to allow declarations like class Color(IntEnum): RED, BLUE, GREEN but with no other logic or expressions in the class body -- all names showing up are strictly part of the sequence, and them have a constructor call, with keyword arguments for all the other cases.
+1 One other important point, that is missed somewhere along Tim's implementation: the repr and str of the enum values should be the value's name - no need to the fancy representation. Actually, the fancy representation defeats most of the purpose of having the constants/enums They could have a property like Enum._qualified_name if one needs it.
Regards
Antoine.

On 12 February 2013 17:50, Barry Warsaw <barry@python.org> wrote:
Not bad - I think that wahtever is agreeded in a nice api that takes advantage of the "class" statement we should have a callabale constructor that takes names as strings. And moreover, it is nice to have a way to load the created enums/constants to the current namespace - and it can be done, without "magic" to the global namespace having a call that takes a "namespace" argument to which one would normally pass in blobals() So: from enum import load, make would allow for:
(I think that allowing a call without the "split()" would be better - ) That would make some *linters complain - but those should update themselves to the fact in time. I consider a way to load the created constants into the module namespace very important to the concept of enums/constant itself. Agreeing on a call that can get "globals()" and update it would preclude the idea of being able to do "from MyEnumClass import *" that showed up on the previous thread. js -><-

On Feb 13, 2013, at 10:08 AM, Steven D'Aprano wrote:
Part of the reason for defining make() the way it is, is so that you can use any iterator in the second argument. I suppose you could special case splitting if the second argument is a single string, but I'm not sure this special case is special enough. -Barry

On Feb 12, 2013, at 04:31 PM, Antoine Pitrou wrote:
(which allows you to build enums programmatically too)
I prefer an alternative API for creating enums programmatically, e.g.: http://pythonhosted.org/flufl.enum/docs/using.html#alternative-api -Barry
participants (25)
-
Antoine Pitrou
-
Barry Warsaw
-
Brett Cannon
-
drekin@gmail.com
-
Eli Bendersky
-
Eric Snow
-
Ethan Furman
-
Georg Brandl
-
Greg Ewing
-
Guido van Rossum
-
Jan Kaliszewski
-
Joao S. O. Bueno
-
Jonathan Slenders
-
Joshua Landau
-
João Bernardo
-
Markus Unterwaditzer
-
Nick Coghlan
-
Paul Moore
-
Richard Oudkerk
-
Ryan Hiebert
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Terry Reedy
-
Tim Delaney
-
Yuval Greenfield