PEP 435 -- Adding an Enum type to the Python standard library

Hello python-dev, We're happy to present the revised PEP 435, collecting valuable feedback from python-ideas discussions as well as in-person discussions and decisions made during the latest PyCon language summit. We believe the proposal is now better than the original one, providing both a wider set of features and more convenient ways to use those features. Link to the PEP: http://www.python.org/dev/peps/pep-0435/ [it's also pasted fully below for convenience]. Reference implementation is available as the recently released flufl.enum version 4.0 - you can get it either from PyPi or https://launchpad.net/flufl.enum. flufl.enum 4.0 was developed in parallel with revising PEP 435. Comments welcome, Barry and Eli ---------------------------------- PEP: 435 Title: Adding an Enum type to the Python standard library Version: $Revision$ Last-Modified: $Date$ Author: Barry Warsaw <barry@python.org>, Eli Bendersky <eliben@gmail.com> Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 2013-02-23 Python-Version: 3.4 Post-History: 2013-02-23 Abstract ======== This PEP proposes adding an enumeration type to the Python standard library. Specifically, it proposes moving the existing ``flufl.enum`` package by Barry Warsaw into the standard library. Much of this PEP is based on the "using" [1]_ document from the documentation of ``flufl.enum``. An enumeration is a set of symbolic names bound to unique, constant values. Within an enumeration, the values can be compared by identity, and the enumeration itself can be iterated over. Decision ======== TODO: update decision here once pronouncement is made. Status of discussions ===================== The idea of adding an enum type to Python is not new - PEP 354 [2]_ is a previous attempt that was rejected in 2005. Recently a new set of discussions was initiated [3]_ on the ``python-ideas`` mailing list. Many new ideas were proposed in several threads; after a lengthy discussion Guido proposed adding ``flufl.enum`` to the standard library [4]_. During the PyCon 2013 language summit the issue was discussed further. It became clear that many developers want to see an enum that subclasses ``int``, which can allow us to replace many integer constants in the standard library by enums with friendly string representations, without ceding backwards compatibility. An additional discussion among several interested core developers led to the proposal of having ``IntEnum`` as a special case of ``Enum``. The key dividing issue between ``Enum`` and ``IntEnum`` is whether comparing to integers is semantically meaningful. For most uses of enumerations, it's a **feature** to reject comparison to integers; enums that compare to integers lead, through transitivity, to comparisons between enums of unrelated types, which isn't desirable in most cases. For some uses, however, greater interoperatiliby with integers is desired. For instance, this is the case for replacing existing standard library constants (such as ``socket.AF_INET``) with enumerations. This PEP is an attempt to formalize this decision as well as discuss a number of variations that were discussed and can be considered for inclusion. Motivation ========== *[Based partly on the Motivation stated in PEP 354]* The properties of an enumeration are useful for defining an immutable, related set of constant values that have a defined sequence but no inherent semantic meaning. Classic examples are days of the week (Sunday through Saturday) and school assessment grades ('A' through 'D', and 'F'). Other examples include error status values and states within a defined process. It is possible to simply define a sequence of values of some other basic type, such as ``int`` or ``str``, to represent discrete arbitrary values. However, an enumeration ensures that such values are distinct from any others including, importantly, values within other enumerations, and that operations without meaning ("Wednesday times two") are not defined for these values. It also provides a convenient printable representation of enum values without requiring tedious repetition while defining them (i.e. no ``GREEN = 'green'``). Module and type name ==================== We propose to add a module named ``enum`` to the standard library. The main type exposed by this module is ``Enum``. Hence, to import the ``Enum`` type user code will run:: >>> from enum import Enum Proposed semantics for the new enumeration type =============================================== Creating an Enum ---------------- Enumerations are created using the class syntax, which makes them easy to read and write. An alternative creation method is described in `Convenience API`_. To define an enumeration, derive from the ``Enum`` class and add attributes with assignment to their integer values:: >>> from enum import Enum >>> class Colors(Enum): ... red = 1 ... green = 2 ... blue = 3 Enumeration values have nice, human readable string representations:: >>> print(Colors.red) Colors.red ...while their repr has more information:: >>> print(repr(Colors.red)) <EnumValue: Colors.red [value=1]> The enumeration value names are available through the class members:: >>> for member in Colors.__members__: ... print(member) red green blue Let's say you wanted to encode an enumeration value in a database. You might want to get the enumeration class object from an enumeration value:: >>> cls = Colors.red.enum >>> print(cls.__name__) Colors Enums also have a property that contains just their item name:: >>> print(Colors.red.name) red >>> print(Colors.green.name) green >>> print(Colors.blue.name) blue The str and repr of the enumeration class also provides useful information:: >>> print(Colors) <Colors {red: 1, green: 2, blue: 3}> >>> print(repr(Colors)) <Colors {red: 1, green: 2, blue: 3}> The ``Enum`` class supports iteration. Iteration is defined as the sorted order of the item values:: >>> class FiveColors(Enum): ... pink = 4 ... cyan = 5 ... green = 2 ... blue = 3 ... red = 1 >>> [v.name for v in FiveColors] ['red', 'green', 'blue', 'pink', 'cyan'] Enumeration values are hashable, so they can be used in dictionaries and sets:: >>> apples = {} >>> apples[Colors.red] = 'red delicious' >>> apples[Colors.green] = 'granny smith' >>> apples {<EnumValue: Colors.green [value=2]>: 'granny smith', <EnumValue: Colors.red [value=1]>: 'red delicious'} To programmatically access enumeration values, use ``getattr``:: >>> getattr(Colors, 'red') <EnumValue: Colors.red [value=1]> Comparisons ----------- Enumeration values are compared by identity:: >>> Colors.red is Colors.red True >>> Colors.blue is Colors.blue True >>> Colors.red is not Colors.blue True >>> Colors.blue is Colors.red False Ordered comparisons between enumeration values are *not* supported. Enums are not integers (but see `IntEnum`_ below):: >>> Colors.red < Colors.blue Traceback (most recent call last): ... NotImplementedError >>> Colors.red <= Colors.blue Traceback (most recent call last): ... NotImplementedError >>> Colors.blue > Colors.green Traceback (most recent call last): ... NotImplementedError >>> Colors.blue >= Colors.green Traceback (most recent call last): ... NotImplementedError Equality comparisons are defined though:: >>> Colors.blue == Colors.blue True >>> Colors.green != Colors.blue True Comparisons against non-enumeration values will always compare not equal:: >>> Colors.green == 2 False >>> Colors.blue == 3 False >>> Colors.green != 3 True >>> Colors.green == 'green' False Extending enumerations by subclassing ------------------------------------- You can extend previously defined Enums by subclassing:: >>> class MoreColors(Colors): ... pink = 4 ... cyan = 5 When extended in this way, the base enumeration's values are identical to the same named values in the derived class:: >>> Colors.red is MoreColors.red True >>> Colors.blue is MoreColors.blue True However, these are not doing comparisons against the integer equivalent values, because if you define an enumeration with similar item names and integer values, they will not be identical:: >>> class OtherColors(Enum): ... red = 1 ... blue = 2 ... yellow = 3 >>> Colors.red is OtherColors.red False >>> Colors.blue is not OtherColors.blue True These enumeration values are not equal, nor do they and hence may exist in the same set, or as distinct keys in the same dictionary:: >>> Colors.red == OtherColors.red False >>> len(set((Colors.red, OtherColors.red))) 2 You may not define two enumeration values with the same integer value:: >>> class Bad(Enum): ... cartman = 1 ... stan = 2 ... kyle = 3 ... kenny = 3 # Oops! ... butters = 4 Traceback (most recent call last): ... ValueError: Conflicting enums with value '3': 'kenny' and 'kyle' You also may not duplicate values in derived enumerations:: >>> class BadColors(Colors): ... yellow = 4 ... chartreuse = 2 # Oops! Traceback (most recent call last): ... ValueError: Conflicting enums with value '2': 'green' and 'chartreuse' Enumeration values ------------------ The examples above use integers for enumeration values. Using integers is short and handy (and provided by default by the `Convenience API`_), but not strictly enforced. In the vast majority of use-cases, one doesn't care what the actual value of an enumeration is. But if the value *is* important, enumerations can have arbitrary values. The following example uses strings:: >>> class SpecialId(Enum): ... selector = '$IM($N)' ... adaptor = '~$IM' ... >>> SpecialId.selector <EnumValue: SpecialId.selector [value=$IM($N)]> >>> SpecialId.selector.value '$IM($N)' >>> a = SpecialId.adaptor >>> a == '~$IM' False >>> a == SpecialId.adaptor True >>> print(a) SpecialId.adaptor >>> print(a.value) ~$IM Here ``Enum`` is used to provide readable (and syntactically valid!) names for some special values, as well as group them together. While ``Enum`` supports this flexibility, one should only use it in very special cases. Code will be most readable when actual values of enumerations aren't important and enumerations are just used for their naming and comparison properties. IntEnum ------- A variation of ``Enum`` is proposed where the enumeration values also subclasses ``int`` - ``IntEnum``. These values can be compared to integers; by extension, enumerations of different types can also be compared to each other:: >>> from enum import IntEnum >>> class Shape(IntEnum): ... circle = 1 ... square = 2 ... >>> class Request(IntEnum): ... post = 1 ... get = 2 ... >>> Shape == 1 False >>> Shape.circle == 1 True >>> Shape.circle == Request.post True However they still can't be compared to ``Enum``:: >>> class Shape(IntEnum): ... circle = 1 ... square = 2 ... >>> class Colors(Enum): ... red = 1 ... green = 2 ... >>> Shape.circle == Colors.red False ``IntEnum`` values behave like integers in other ways you'd expect:: >>> int(Shape.circle) 1 >>> ['a', 'b', 'c'][Shape.circle] 'b' >>> [i for i in range(Shape.square)] [0, 1] For the vast majority of code, ``Enum`` is strongly recommended. Since ``IntEnum`` breaks some semantic promises of an enumeration (by being comparable to integers, and thus by transitivity to other unrelated enumerations), it should be used only in special cases where there's no other choice; for example, when integer constants are replaced with enumerations and backwards compatibility is required with code that still expects integers. Pickling -------- Enumerations created with the class syntax can also be pickled and unpickled:: >>> from enum.tests.fruit import Fruit >>> from pickle import dumps, loads >>> Fruit.tomato is loads(dumps(Fruit.tomato)) True Convenience API --------------- The ``Enum`` class is callable, providing the following convenience API:: >>> Animals = Enum('Animals', 'ant bee cat dog') >>> Animals <Animals {ant: 1, bee: 2, cat: 3, dog: 4}> >>> Animals.ant <EnumValue: Animals.ant [value=1]> >>> Animals.ant.value 1 The semantics of this API resemble ``namedtuple``. The first argument of the call to ``Enum`` is the name of the enumeration. The second argument is a source of enumeration value names. It can be a whitespace-separated string of names, a sequence of names or a sequence of 2-tuples with key/value pairs. The last option enables assigning arbitrary values to enumerations; the others auto-assign increasing integers starting with 1. A new class derived from ``Enum`` is returned. In other words, the above assignment to ``Animals`` is equivalent to:: >>> class Animals(Enum): ... ant = 1 ... bee = 2 ... cat = 3 ... dog = 4 Examples of alternative name/value specifications:: >>> Enum('Animals', ['ant', 'bee', 'cat', 'dog']) <Animals {ant: 1, bee: 2, cat: 3, dog: 4}> >>> Enum('Animals', (('ant', 'one'), ('bee', 'two'), ('cat', 'three'), ('dog', 'four'))) <Animals {dog: four, ant: one, cat: three, bee: two}> The second argument can also be a dictionary mapping names to values:: >>> levels = dict(debug=10, info=20, warning=30, severe=40) >>> Enum('Levels', levels) <Levels {debug: 10, info: 20, warning: 30, severe: 40}> Proposed variations =================== Some variations were proposed during the discussions in the mailing list. Here's some of the more popular ones. Not having to specify values for enums -------------------------------------- Michael Foord proposed (and Tim Delaney provided a proof-of-concept implementation) to use metaclass magic that makes this possible:: class Color(Enum): red, green, blue The values get actually assigned only when first looked up. Pros: cleaner syntax that requires less typing for a very common task (just listing enumeration names without caring about the values). Cons: involves much magic in the implementation, which makes even the definition of such enums baffling when first seen. Besides, explicit is better than implicit. Using special names or forms to auto-assign enum values ------------------------------------------------------- A different approach to avoid specifying enum values is to use a special name or form to auto assign them. For example:: class Color(Enum): red = None # auto-assigned to 0 green = None # auto-assigned to 1 blue = None # auto-assigned to 2 More flexibly:: class Color(Enum): red = 7 green = None # auto-assigned to 8 blue = 19 purple = None # auto-assigned to 20 Some variations on this theme: #. A special name ``auto`` imported from the enum package. #. Georg Brandl proposed ellipsis (``...``) instead of ``None`` to achieve the same effect. Pros: no need to manually enter values. Makes it easier to change the enum and extend it, especially for large enumerations. Cons: actually longer to type in many simple cases. The argument of explicit vs. implicit applies here as well. Use-cases in the standard library ================================= The Python standard library has many places where the usage of enums would be beneficial to replace other idioms currently used to represent them. Such usages can be divided to two categories: user-code facing constants, and internal constants. User-code facing constants like ``os.SEEK_*``, ``socket`` module constants, decimal rounding modes and HTML error codes could require backwards compatibility since user code may expect integers. ``IntEnum`` as described above provides the required semantics; being a subclass of ``int``, it does not affect user code that expects integers, while on the other hand allowing printable representations for enumeration values:: >>> import socket >>> family = socket.AF_INET >>> family == 2 True >>> print(family) SocketFamily.AF_INET Internal constants are not seen by user code but are employed internally by stdlib modules. These can be implemented with ``Enum``. Some examples uncovered by a very partial skim through the stdlib: ``binhex``, ``imaplib``, ``http/client``, ``urllib/robotparser``, ``idlelib``, ``concurrent.futures``, ``turtledemo``. In addition, looking at the code of the Twisted library, there are many use cases for replacing internal state constants with enums. The same can be said about a lot of networking code (especially implementation of protocols) and can be seen in test protocols written with the Tulip library as well. Differences from PEP 354 ======================== Unlike PEP 354, enumeration values are not defined as a sequence of strings, but as attributes of a class. This design was chosen because it was felt that class syntax is more readable. Unlike PEP 354, enumeration values require an explicit integer value. This difference recognizes that enumerations often represent real-world values, or must interoperate with external real-world systems. For example, to store an enumeration in a database, it is better to convert it to an integer on the way in and back to an enumeration on the way out. Providing an integer value also provides an explicit ordering. However, there is no automatic conversion to and from the integer values, because explicit is better than implicit. Unlike PEP 354, this implementation does use a metaclass to define the enumeration's syntax, and allows for extended base-enumerations so that the common values in derived classes are identical (a singleton model). While PEP 354 dismisses this approach for its complexity, in practice any perceived complexity, though minimal, is hidden from users of the enumeration. Unlike PEP 354, enumeration values should only be tested by identity comparison. This is to emphasize the fact that enumeration values are singletons, much like ``None``. Acknowledgments =============== This PEP describes the ``flufl.enum`` package by Barry Warsaw. ``flufl.enum`` is based on an example by Jeremy Hylton. It has been modified and extended by Barry Warsaw for use in the GNU Mailman [5]_ project. Ben Finney is the author of the earlier enumeration PEP 354. References ========== .. [1] http://pythonhosted.org/flufl.enum/docs/using.html .. [2] http://www.python.org/dev/peps/pep-0354/ .. [3] http://mail.python.org/pipermail/python-ideas/2013-January/019003.html .. [4] http://mail.python.org/pipermail/python-ideas/2013-February/019373.html .. [5] http://www.list.org Copyright ========= This document has been placed in the public domain. Todo ==== * Mark PEP 354 "superseded by" this one, if accepted .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 coding: utf-8 End:

On Fri, Apr 12, 2013 at 2:55 PM, Eli Bendersky <eliben@gmail.com> wrote:
I like much of this PEP, but the exception type for this case seems odd to me. Wouldn't a TypeError be more appropriate here? Somewhat like this:
Cheers, Dirkjan

On Fri, 12 Apr 2013 11:02:54 -0400, Barry Warsaw <barry@python.org> wrote:
I think TypeError is more consistent with the rest of Python:
You get that automatically if you return NotImplemented from the comparison methods. I don't think you should be explicitly raising NotImplemented. --David

On Apr 12, 2013, at 11:29 AM, R. David Murray wrote:
Oh darn, this doesn't work for Python 2.7. You don't care for PEP 435, but flufl.enum will have to raise the TypeError explicitly for consistency. -Barry

On 04/12/2013 08:37 AM, Barry Warsaw wrote:
Yeah, 2.x has everything ordered; returning NotImplemented only works for the non-comparison methods (such as add, subtract, etc.); one of the nice things about 3.x. :) -- ~Ethan~

On 04/12/2013 08:02 AM, Barry Warsaw wrote:
NotImplemented makes it seem like we could implement it in a subclass -- is this true? Also, for a more direct comparison: --> 'a' < 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: str() < int() I would think this is the more appropriate exception and text to use. -- ~Ethan~

On Fri, Apr 12, 2013 at 8:02 AM, Barry Warsaw <barry@python.org> wrote:
Interesting. I'm having a hard time articulating why, but NotImplementedError just feels more right to me in this case.
To me, NotImplementedError means that a subclass didn't implement something it should have implemented. But that's not the case here, is it? It's not a bug in the class, it's a bug in the call site. So I agree it ought to be TypeError. -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 12, 2013 at 9:34 AM, Guido van Rossum <guido@python.org> wrote:
Seems I was late to this particular argument. :-) Anyway, as far as I can tell the PEP looks great. I personally think it would be nicer if regular Enums were ordered (as long as the underlying values are ordered), but I don't care enough to overrule the FLUFL. I do wonder about this passage in the PEP:
I don't understand what this has to do with storing enums in a database. But it reminded me that for the purpose of storing enums in a database, it would be nice to have two examples: one that stores the names and looks them up (do you really have to use getattr() for that?), and one that stores the values and looks them up (how do you do that at all?). Should the metaclass-based API used to create IntEnum be documented, so strongly motivated people can write their own crazy variants? -- --Guido van Rossum (python.org/~guido)

In article <CAP7+vJLUO0B0y+=Jcg8D=jQ3mggSAm1tb9ZzttO7nCrKE6Mh6Q@mail.gmail.com>, Guido van Rossum <guido@python.org> wrote:
I, too, would strongly prefer to see ordering within an enum. I use home-made enums heavily in my code and find ordering comparisons useful there. Using intEnum is certainly doable, but that opens up the door to comparing values from different Enums, which is not something I'd want to allow. I don't understand why order tests are seen as a bad thing, as long as the values have order (as they will in the common cases of string and int values). It seems the code must go out of its way to prohibit such tests. In any case, I'm very pleased to have this library. it will get rid of much boilerplate in my code. It seems very well designed, and I'm really glad to see it supports subclassing to add values! -- Russell

On Apr 12, 2013, at 11:21 AM, Russell E. Owen wrote:
This was all hashed out in gory detail on python-ideas. I feel strongly that base EnumValues should be unordered, especially because the underlying values can be of any type. What would you expect to happen in this case: class X(Enum): a = 1 b = 'hi' if X.a < myvalue < X.b: # whaa? I think for most use cases, IntEnums will fit the bill for those who want ordered comparisons, and it's also easy to subclass EnumValues to specialize the behavior (in fact, this is how IntEnums are implemented). So if you really want ordered-comparisons-with-untyped-enum-values, you can have them. :) -Barry

On Fri, 12 Apr 2013 15:33:02 -0400, Barry Warsaw <barry@python.org> wrote:
You are right, the problem of comparison of disparate types makes ordering a non-starter. But by the same token that means you are going to have to be consistent and give up on having a sorted iteration and a stable repr:
--David

On Fri, Apr 12, 2013 at 1:52 PM, R. David Murray <rdmurray@bitdance.com>wrote:
I actually think that having values with different types within a single Enum is conceptually wrong and should be disallowed at creation time. With enums, you either care or don't care about their actual value. If you don't care (the most common use case of an enum, IMHO), then no problem here. If you do care, then it's probably for very specific reasons most of which are solved by IntEnum. I can't imagine why someone would need differently typed values in a single enum - this just seems like a completely inappropriate use of an enum to me. Eli

On Fri, 12 Apr 2013 14:06:55 -0700, Eli Bendersky <eliben@gmail.com> wrote:
I'm sure someone will come up with one :) But seriously, even if you require all values to be of the same type, that doesn't solve the sorting problem:
Now, you could *further* require that the type of enum values be sortable....but that point you really have no excuse for not allowing enum values to be compared :) --David

So, pragmatically, if e and f are values of the same enum class, couldn't e <cmp> f (where <cmp> is any comparison operator) defer to e.value <cmp> f.value ? Or is the problem with <cmp> being == and e and f being different enum values with the same underlying value? But that's already iffy, isn't it? (Barry's favorite solution for serializing to a database wouldn't work either.) And they could still be distinguished by using 'is' instead of '=='. On Fri, Apr 12, 2013 at 2:17 PM, R. David Murray <rdmurray@bitdance.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On Apr 12, 2013, at 02:34 PM, Guido van Rossum wrote:
If I'm parsing that correctly, yes, I think we don't want to defer to the enum.value for the base enum type because unrelated enumeration values should not compare equal. E.g. class Colors(Enum): red = 1 blue = 2 green = 3 class Animals(Enum): ant = 1 bee = 2 cat = 3 In this case, Animals.bee != Colors.blue. Of course, they would be == if they derived from IntEnums. While I personally recommend and use identity to compare enum types, it seems to be difficult to convince other users to also do so. We could enforce this by not implementing __eq__ and __ne__, but it seems worse to disallow this than to make it work. E.g. if shape.color is Colors.red: # works if shape.color == Colors.red: # throws an exception -Barry

On Fri, Apr 12, 2013 at 3:44 PM, Barry Warsaw <barry@python.org> wrote:
No, my proposal was only meant for if the classes are the same. If the classes are different the comparison should always fail.
Right, I *only* meant this as a way to differentiate between bee and wasp in: class Insect(Enum): wasp = 1 bee = 1 ant = 2 We'd have Insect.wasp == Insect.bee < Insect.ant but Insect.wasp is not Insect.bee. -- --Guido van Rossum (python.org/~guido)

On 13/04/13 10:13, Glenn Linderman wrote:
can't define two names in the same enum to have the same value, per the PEP.
I think that's too strong a restriction. I would expect to be able to do this: class Insect(Enum): wsap = 1 # Oops, needed for backward compatibility, do not remove. wasp = 1 # Preferred. Use this in new code. bee = 2 ant = 3 Or at the very least: class Insect(Enum): wasp = wsap = 1 bee = 2 ant = 3 I googled on "C enum" and the very first hit includes a duplicate value: http://msdn.microsoft.com/en-AU/library/whbyts4t%28v=vs.80%29.aspx And two examples from asm-generic/errno.h: #define EWOULDBLOCK EAGAIN /* Operation would block */ #define EDEADLOCK EDEADLK What's the justification for this restriction? I have looked in the PEP, and didn't see one. -- Steven

On 4/12/2013 10:51 PM, Steven D'Aprano wrote:
That's actually even better of an example than you may have realized because historically EWOULDBLOCK != EAGAIN[1]. So, there very well may need to exist such code as: if <some hairy platform>: _EAGAIN = <X> _EWOULDBLOCK = <Y> else: _EAGAIN = _EWOULDBLOCK = <X> class Errno(Enum): EAGAIN = _EAGAIN EWOULDBLOCK = _EWOULDBLOCK I don't think it's all that uncommon that enum values that represent states of a system get merged or renamed over time, and this one is a great example of that. [1] http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Error-Codes.... -- Scott Dial scott@scottdial.com

On Apr 13, 2013, at 12:51 PM, Steven D'Aprano wrote:
If you allowed this, there would be no way to look up an enumeration item by value. This is necessary for e.g. storing the value in a database. If you know that the "insect" column is an INTEGER that represents an enumeration item of Insect, then you can just store the int value in the column. To reconstitute the actual enumeration item when you read the column back from the database, you need to be able to look up the item by value. Currently, you do this: >>> my_insect = Insect[database_value] but if the values are not unique, you have no way to reliably do it. I don't much like APIs which return sequences (either always or "on demand") or rely on definition order or some other arbitrary discrimination because I don't think any of those are practical in the real world. I also recall that duplication was a specific anti-feature in the previous python-ideas discussion. It was thought that this would allow for typos in the set of enumeration items to creep in. I don't see how you can reconcile these issues to allow for duplicate values. -Barry

On 21/04/13 05:42, Barry Warsaw wrote:
I agree that's a good example of a situation where the user might want unique values. But I don't agree that this is the responsibility of the Enum type itself. Enums are a mapping from name to value, just like dicts: d = {'wasp': 1, 'bee': 2, 'ant': 3} There are use-cases where we might want dicts to be 1:1 too, but we don't force that restriction on all dicts. Even if I want to reconstruct the key from the value, doesn't mean that everybody who uses dicts must be prohibited from using duplicate values. We don't even offer a guaranteed 1:1 mapping type in the standard library. Actual real world enums can and do frequently contain duplicate values, I've previously given examples of such. The ability to have two enums with the same value is in my opinion not just a Nice To Have but is a Must Have. With the ability to have duplicate values, enums are the One Obvious Way to do it. Without it, the decision process becomes more complex: does my application now, or will it ever, need duplicate values? If there's even a tiny chance it might need them in the future, I cannot risk getting stuck with an enum type that prohibits that. I would argue that it is the responsibility of enums to start with the least restrictions as is reasonable, and leave additional restrictions up to subclasses, rather than the other way around. (I'll just quietly mention the Liskov Substitution Principle here...) If some people need enums to have unique values, then enforcing that should be their responsibility, and not forced on all users whether they need that restriction or not. If there is enough demand for that, then perhaps the enum module could provide a standard mechanism for enforcing unique values, via a flag, or a subclass, say. I like the idea of a mixin: class Insect(UniqueValues, Enum): wasp = 1 bee = 2 ant = 3 But it should be off by default.
Neither do I. I would be perfectly happy for enums to raise ValueError in that case, and leave it up to the caller to either prevent duplicate values from occurring, or to deal with the exception in whichever way makes sense for their application.
Typos occur whether enums allow duplicate values or not. How you deal with such typos depends on whether you are forced to keep it forever, or can define a second enum with the same value and deprecate the typo. -- Steven

On Sun, Apr 21, 2013 at 11:29 AM, Steven D'Aprano <steve@pearwood.info> wrote:
The PEP is fine, as it already allows duplicate names without encouraging them: class Insect(Enum): wasp = 1 # Preferred. Use this in new code. bee = 2 ant = 3 # Backwards compatibility aliases Insect.wsap = Insect.wasp If you have a lot of such aliases: aliases = { "wasp": ["wsap"], ... } for attr, names in aliases.items(): for name in names: setattr(Insect, name, getattr(Insect, attr)) A more concise syntax for handling duplicates may prove desirable at some point in the future, but this is a case where encouraging correctness by default is a good idea. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Barry Warsaw wrote:
On Apr 13, 2013, at 12:51 PM, Steven D'Aprano wrote:
Hm. What you really want there isn't two enum objects with the same value, but two names bound to the same enum object. Then looking it up by value would not be a problem. -- Greg

On 21 April 2013 21:02, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
If there were some way to identify the canonical name a lookup by value would be unambiguous. If we have iteration in definition order, I'd say the first defined name for a value should be the canonical name, and any other name for the value should be considered an alias. That would preclude the syntax above, but the following might be workable: class Insect(Enum): wasp = 1 bee = 2 ant = 3 # aliases wsap = wasp waps = 1 In the above, looking up by the value 1 would always return Insect.wasp. Tim Delaney

On 13.04.13 03:13, Glenn Linderman wrote:
For current flufl.enum implementations this requires values to be hashable. An alternative implementation can use comparability (which already required for repr() and iteration).

On Fri, Apr 12, 2013 at 2:17 PM, R. David Murray <rdmurray@bitdance.com>wrote:
Which is precisely the reason to ban it :)
I'm actually not really in favor of enum values being comparable. I think this is more a C-ism and does not cleanly settle with my concept of what an enum is. For comparable enums and other C-derived properties, IntEnum is out there, so call it maybe ;-) Eli

On Apr 12, 2013, at 05:17 PM, R. David Murray wrote:
I'd be more willing to give up on sorting for the base enum type's iteration and repr. It's not crucial functionality whereas I still don't want the base enum values to support ordered comparisons. -Barry

On 13 April 2013 08:32, Barry Warsaw <barry@python.org> wrote:
Just using definition order as the stable iteration order would do the trick - no need for any comparisons at all. Subclasses (e.g. IntEnum) can then override it. You could then easily have a subclass that implemented comparisons defined based on iteration order. It makes sense not to have this in the base Enum class (it would be confusing). On a related note, I really would like to have the ordinal exposed if this were added. Tim Delaney

On Apr 13, 2013, at 08:37 AM, Tim Delaney wrote:
I think this isn't possible if we want to keep backward compatibility with earlier Pythons, which I want to do. OTOH, we have another natural sorting order for base Enums sitting right in front of us: the attribute name. These have to be unique and ordered, so why not use this for both the __repr__() and the base Enum __iter__()? IntEnum can override __iter__() to iterate over item values, which also must be ordered. I just made this change to flufl.enum and it seems to work well.
-Barry

On Sat, 20 Apr 2013 14:10:32 -0400, Barry Warsaw <barry@python.org> wrote:
I think definition order would be much better, but if we can't have that, this is probably better than value order for non-int. --David

Can we separate the iteration order and the comparison order? For iteration order, I think by definition order or by attribute name are both great, and better than by value. But for comparing values using <, ==, >, I strongly feel we should defer to the underlying values -- if those cannot be compared, then the enums can't either, but the iteration order is still defined. On Sat, Apr 20, 2013 at 11:26 AM, R. David Murray <rdmurray@bitdance.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On 21 April 2013 04:10, Barry Warsaw <barry@python.org> wrote:
Do you want it compatible with Python 2.x? In that case I don't see a way to do it - getting definition order relies on __prepare__ returning an ordered dict, and __prepare__ of course is only available in 3.x. Tim Delaney

On Sun, Apr 21, 2013 at 9:10 AM, R. David Murray <rdmurray@bitdance.com> wrote:
Agreed. I think the stdlib enum library should use __prepare__ and iterate in definition order (since 2.x compatibility isn't of any concern), while flufl.enum can use "sorted by name" as the iteration order. An "order_by_name" keyword argument to __prepare__ in the stdlib version could then allow the user to opt in to the flufl.enum behaviour, while still using definition order by default. As in: class DefinitionOrder(enum.Enum): first = 1 second = 2 third = 3 list(DefinitionOrder) -> [DefinitionOrder.first, DefinitionOrder.second, DefinitionOrder.third] And: class NameOrder(enum.Enum, order_by_name=True): a = 1 c = 2 b = 3 list(NameOrder) -> [NameOrder.a, NameOrder.b, NameOrder.c] flufl.enum could also offer the "order_by_name" flag on 3.x, but set it to "True" by default. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Apr 21, 2013, at 03:25 PM, Nick Coghlan wrote:
Seriously, why all the extra complexity for something you'll never care about? You don't care about the exact contents of __repr__ as long as it's predictable. You don't really care about item iteration order either as long as it's defined. Sorting by attribute name fulfills both use cases *and* it's simple. Zen 3 FTW. -Barry

On Apr 20, 2013, at 07:10 PM, R. David Murray wrote:
It seems strange to limit a new Python3 feature to the Python2 feature set. Just saying :)
For a critical feature sure, but I don't put __repr__ or enum item iteration order in that category. There's no need for gratuitous incompatibility either, and attribute name order is just fine. -Barry

On 22 Apr 2013 07:50, "Barry Warsaw" <barry@python.org> wrote:
iteration
order in that category. There's no need for gratuitous incompatibility either, and attribute name order is just fine.
Iteration order matters a lot if you don't want people complaining about enums being broken: class Days(enum.Enum): Monday = 1 Tuesday = 2 Wednesday = 3 Thursday = 4 Friday = 5 Saturday = 6 Sunday = 7 Cheers, Nick.
http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com

On 22 April 2013 09:02, Nick Coghlan <ncoghlan@gmail.com> wrote:
I'm fine with iteration order being by sorted name by default, so long as it's easily overrideable by enum subclasses or metaclasses e.g. an IntEnum should probably iterate in value order. For definition order, a 3.x-only metaclass could be provided: class Days(enum.Enum, metaclass=enum.DefinitionOrder): Monday = 1 Tuesday = 2 Wednesday = 3 Thursday = 4 Friday = 5 Saturday = 6 Sunday = 7 Tim Delaney

On Apr 22, 2013, at 09:31 AM, Tim Delaney wrote:
It does</timemachine>. :)
Yep, that's how it works. From flufl.enum: class IntEnumMetaclass(EnumMetaclass): # Define an iteration over the integer values instead of the attribute # names. def __iter__(cls): for key in sorted(cls._enums): yield getattr(cls, cls._enums[key]) IntEnum = IntEnumMetaclass(str('IntEnum'), (Enum,), { '__doc__': 'A specialized enumeration with values that are also integers.', '__value_factory__': IntEnumValue, }) -Barry

On 22 April 2013 10:31, Barry Warsaw <barry@python.org> wrote:
I knew it *did*, but wasn't sure if with the current discussion it was going to continue to do so.
Would it be worthwhile storing a sorted version of the enum keys here? Or do you think the current space vs speed tradeoff is better? I need to grab the current flufl.enum code and see if I can easily extend it to do some more esoteric things that my enum implementation supports (*not* bare names, but maybe the name = ... syntax, which of course requires the definition order metaclass). I'm in the middle of a release cycle, so my time is somewhat limited right now :( Tim Delaney

On Sun, Apr 21, 2013 at 5:55 PM, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
In any case I think it would be odd if IntEnum used a different policy than Enum. That would be a disturbing difference in behavior between two otherwise rather similar classes.
I'm convinced. I also think that 2/3 compatibility is not as important as getting it right for the foreseeable future. -- --Guido van Rossum (python.org/~guido)

On Apr 22, 2013, at 10:55 AM, Tim Delaney wrote:
Would it be worthwhile storing a sorted version of the enum keys here? Or do you think the current space vs speed tradeoff is better?
It's an implementation detail that doesn't bother me too much either way. -Barry

On Apr 22, 2013, at 09:02 AM, Nick Coghlan wrote:
Sorry, that's still not a complete use case. I don't see why you'd depend on iteration order over Days for any particular functionality. Besides, if you did, I think it would be better to derive Days from IntEnum and then iteration order is guaranteed over the values. -Barry

On Mon, Apr 22, 2013 at 10:28 AM, Barry Warsaw <barry@python.org> wrote:
You mean other than printing the days of the week in order without needing to worry about the specific values assigned to them? Using sort-by-name also introduces other weirdness, such as subclasses potentially inserting their values in the middle of inherited names, rather than appending to the end as one might reasonably expect. While using sort-by-name is better than not providing a consistent ordering at all, using definition order is substantially less surprising than sorting by key name, and PEP 3115 and collections.OrderedDict makes that easy to support in Python 3.x. The fact that this will make for a behavioural difference between the standard library and flufl.enum does *not* count as an argument for making the behaviour of the standard library version less intuitive (if that was a valid argument, the 3.3+ ipaddress module would look a *lot* more like it's ipaddr inspiration).
But only at the cost of breaking the other guarantees provided by a standard enumeration. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Apr 22, 2013, at 10:42 AM, Nick Coghlan wrote:
My point is, "days of the week" has a natural ordering, so why wouldn't you use IntEnum for that? Problem solved. There's no natural ordering for things like colors or animals, so the values don't matter. I claim that neither does the repr or iteration order except that the former should be *predictable* and it would be nice to define the latter, but that's not actually necessary. Undefined iteration order would be just as fine for Enum.
I don't see how iteration order could affect how you'd write the derived class syntax.
Maybe. If they care about iteration order at all, then they will have to special case their code for Python's < 3.3, which means they'll probably have to explicitly sort it anyway for the foreseeable future. I guess it'll be moot in 10 years though. ;) They probably don't care about iteration order, which I think will be the common case (heck, iteration over the enum will be pretty rare *anyway*). -Barry

On 04/25/2013 03:23 PM, Barry Warsaw wrote:
Because by using an IntEnum we lose the Enum type protection, which is one of the reasons to want an Enum type to begin with.
People like things sorted (or am I alone here?) There are three obvious natural orderings: 1 - value 2 - definition order 3 - name And that's my order of preference for them.
It probably wouldn't, but if I had: --> class Color(Enum): ... red = 1 ... blue = 2 ... green = 3 --> class MoreColor(Color): ... cyan = 4 ... magenta = 5 ... yellow = 6 I would be very unhappy with: --> list(MoreColor) [ MoreColor.blue, MoreColor.cyan, MoreColor.green, MoreColor.magenta, MoreColor.red, MoreColor.yellow, ] because 1) it's not the order I defined it in; and 2) it's not in value order. -- ~Ethan~

On 4/25/2013 3:23 PM, Barry Warsaw wrote:
My point is, "days of the week" has a natural ordering, so why wouldn't you use IntEnum for that? Problem solved.
While the ordering is natural, some implementations start from 0, some start from 1, and on the naming side, some start from Sunday, and some start from Monday. So there are lots of opportunities for the numbers vary, and having easy conversions to int which expose those numbers and allow comparisons to integers, is adding problems, not solving them. On 4/25/2013 3:36 PM, Ethan Furman wrote:
So, being blissfully unaware of the implementation issues, not having yet read the implementation, I suggest that the preferred iteration order should be a characteristic of the Enum when defined, and/or that there should be a method to obtain a list in any of the natural orderings (specifying value ordering may add a restriction that the values be orderable, if done at definition time; if done only on retrieval, attempting to access the list by value would raise an exception). Another possible ordering would be "random", and for enumerations with values that are tuples or lists or hashes, ordering by some specified element would be conceivable. On the other hand, except for "definition" order, all the other possible orderings could be derived externally to the enumeration. So if enumerations preserve/provide "definition" order, all the others could be implemented externally or as subtypes. "Definition" order, then is the basic ordering for enumerations because it cannot be externally derived, other than by reading the source code.

On 04/25/2013 06:03 PM, MRAB wrote:
Lots of counting systems wrap: seconds, minutes, hours, days of week, days of month, days of year, millimeters, inches, etc., etc., and so forth. We still apply ordering to them, and talk about 15 <whatevers> being less than 42 <whatevers>. -- ~Ethan~

On 26/04/2013 1:15 p.m., Ethan Furman wrote:
Lots of counting systems wrap: seconds, minutes, hours, days of week, days of month, days of year, millimeters, inches, etc., etc.
But we don't disagree on which is the first minute of an hour.
We still apply ordering to them, and talk about 15 <whatevers> being less than 42 <whatevers>.
When we do that, we're using cardinal numbers (how many), not ordinal numbers (what order). -- Greg

On 26/04/13 13:03, MRAB wrote:
But there _is_ an ordering problem, in that the days wrap around.
Do we want a CircularEnum, then? Ordering would be defined only up to the starting value, which you would be required to specify when doing anything where it mattered. class Day(CircularEnum): sunday = 0 monday = 1 ... saturday = 6 list(Day.startingat(Day.tuesday)) --> [Day.tuesday, Day,wednesday, Day.thursday, Day.friday, Day.saturday, Day.sunday, Day.monday] Modular arithmetic would apply, so Day.saturday + 3 --> Day.tuesday That would be the replacement for Day(3), which would be disallowed. -- Greg

On 26/04/2013 1:28 p.m., Ethan Furman wrote:
Interesting idea, but why does Day(3) have to be disallowed to make it work?
Because it's ambiguous. Which day of the week is number 3? It depends on where you start. I should perhaps point out that the numbers assigned to the values initially are just to establish the relative ordering. They wouldn't be directly accessible once the values are created. To get an integer value corresponding to a Day value, you would have to do arithmetic: Day.wednesday - Day.sunday --> 3 -- Greg

On Sun, 21 Apr 2013 20:28:16 -0400, Barry Warsaw <barry@python.org> wrote:
I didn't read Nick's message as being about a use case. The key word in the sentence above was "complaining" :) Regardless of the specific values involved, it is pretty much guaranteed that if anything other than definition order is used we *will* get bug reports/enhancement requests to fix it, on a regular basis. We can choose to live with that, but we should admit that it will will happen :) --David

On 13/04/13 05:33, Barry Warsaw wrote:
I would expect the same behaviour from enums that I get in Python 3 from non-enums. That is, if enums X.a and X.b happen to both be ints, or both strings, then comparisons should succeed, but if they are different types, I should get a TypeError. The above applies to related enums. If they are unrelated (e.g. Colours.red < Insects.ant) then I think TypeError is appropriate. -- Steven

On Apr 12, 2013, at 09:43 AM, Guido van Rossum wrote:
Not much, really. It's just hold over text from the original motivation for exposing the enum class as an attribute on the values. In Mailman, I store these values in my database and they get reconstituted correctly by the ORM layer. Anyway, in this particular case, I think the motivation is unnecessary for describing the API, so I'll remove that from the PEP.
It's going to be dependent on how you store and retrieve enum values. As an example, in my database layer I store the enum values in an integer column, with the ORM layer knowing which Enum subclass to use. So all I need to do to store the value is ``int(enum_value)`` and to get back the original enum value, I just do ``self._enum[int_value]`` where self._enum is the Enum subclass. To me, that's probably the most common way of doing it. If you store by name though, yes, you'd have to use ``getattr(self._enum, name)``. At one point Enums also supported getitem syntax for lookup by name, but consider this case: class Fruit(Enum): apple = 'red' banana = 'yellow' tangerine = 'orange' orange = 'reddish yellow' What should Fruit['orange'] return? In private email Nick pointed out that using getattr() for lookup by name works fine, and getitem for look up by value has been in the API since the beginning, so now Fruit['orange'] is documented to return Fruit.tangerine, i.e. lookup by value only. (Actually, in flufl.enum, lookup-by-name is still there but deprecated. We can just drop it for Python 3.4).
Should the metaclass-based API used to create IntEnum be documented, so strongly motivated people can write their own crazy variants?
I think you recommended against that in python-ideas :). -Barry

On Fri, Apr 12, 2013 at 12:26 PM, Barry Warsaw <barry@python.org> wrote:
Yeah, it looked like an editing mistake. :-)
Agreed. I can't easily find that in the PEP though. It doesn't mention __getitem__ and I can't find any examples of using <enumclass>[<int>].
Yeah, getattr() is good enough, and it is documented in the PEP.
I have changed my mind; I am now at least +0 on documenting the metaclass craziness. -- --Guido van Rossum (python.org/~guido)

On Apr 12, 2013, at 12:56 PM, Guido van Rossum wrote:
Agreed. I can't easily find that in the PEP though. It doesn't mention __getitem__ and I can't find any examples of using <enumclass>[<int>].
Indeed, this looks like an omission in the PEP. flufl.enum's usage documentation definitely talks about this: http://pythonhosted.org/flufl.enum/docs/using.html specifically: http://pythonhosted.org/flufl.enum/docs/using.html#conversions Eli, care to add this to the PEP?
It would be fine with me. I left it out of the flufl.enum docs and we left it out of the PEP after your original comments, but I'm pretty happy with the API and can't foresee us changing it (famous last words). FWIW, we use a special attribute called __value_factory__ on the Enum subclass to name the class used to create enum values. This is all there is to IntEnum: class IntEnum(Enum): """A specialized enumeration with values that are also integers.""" __value_factory__ = IntEnumValue and even the IntEnumValue class isn't that big. It can be even smaller in Python 3.4 because of the workarounds in flufl.enum for Python 2 compatibility, and deprecations. Eli, what do you think about documenting the extension API? Cheers, -Barry

On Fri, Apr 12, 2013 at 3:23 PM, Barry Warsaw <barry@python.org> wrote:
Done. getattr did get a mention there, but now I made it more prominent and described __getitem__ access as well.
I don't have major objections... Eli

Eli Bendersky <eliben@gmail.com> writes:
These enumeration values are not equal, nor do they and hence may exist in the same set, or as distinct keys in the same dictionary::
I'm not a native speaker and I found the above difficult to parse: is there anything missing between "nor do they" and "and hence may exist"? ciao, lele. -- nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia. lele@metapensiero.it | -- Fortunato Depero, 1929.

On Apr 12, 2013, at 03:53 PM, Lele Gaifax wrote:
Oops, yes. Good catch! I changed it to: Because ``Colors`` and ``OtherColors`` are unrelated enumerations, their values are not equal, and thus they may exist in the same set, or as distinct keys in the same dictionary:: -Barry

On Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> wrote:
Link to the PEP: http://www.python.org/dev/peps/pep-0435/ [it's also pasted fully below for convenience].
This looks great. There's just one bit I don't understand. I'm sure it was discussed in the python-ideas thread, but the discussion of it in the PEP does not provide any motivation for the decision.
[...]
This is the part that I don't understand. Enums *clearly* have an ordering, since the iteration order is defined and stable. Why should I not be allowed to compare values from the same Enum type? There are certainly use cases where that is very useful. To give you a concrete use case: consider response codes from a client server application constructed the way typical internet protocols are. We might have: class MyAppCode(Enum): ok = 200 command_complete = 201 status_response = 202 help_response = 203 service_ready = 204 signoff_accepted = 205 temporary_failure = 400 service_not_available = 401 server_error = 402 out_of_resources = 403 error = 500 syntax_error = 501 command_not_implemented = 502 access_denied = 503 resource_not_found = 504 It can be quite handy to be able to say things like: code = myapp.operation(opstring) if MyAppCode.temporary_failure < code < MyAppCode.error: myframework.requeue(opstring, code=code) return False elif code > MyAppCode.error: raise AppError(code) .... In talking to an existing internet protocol it would be natural to use IntEnum and this issue would not arise, but I have recently worked on an application that had *exactly* the above sort of enumeration used internally, when it would have been totally appropriate to use Enum rather than IntEnum. The ap has several places where an ordered comparison against the enum is used to check if a code is in the error range or not. --David

I think the reason they are not supporting __lt__, __gt__,etc. is because ints are optional values for enums, therefore it wouldnt be a good idea to compare enums of different types in that way. example:
and doing
MyEnum.fir >= MyEnum.thir would give unexpected results, therefore not making it a great idea
On Fri, Apr 12, 2013 at 9:58 AM, R. David Murray <rdmurray@bitdance.com>wrote:

On Fri, 12 Apr 2013 10:19:29 -0400, Davis Silverman <sinistersnare@gmail.com> wrote:
That's why I included the bit about iterating the values. The ordering *is* defined. I find it much more surprising for that ordering to be inaccessible via the comparison operators. I think either the iteration order should be undefined (like a set or dict), or the comparison operations should work. I'd prefer the latter, because of the use case I outlined. --David

i agree with that, and good point, i guess i misunderstood. enums could be a nice edition, i cant wait to see how this goes. --Sinistersnare On Fri, Apr 12, 2013 at 10:23 AM, R. David Murray <rdmurray@bitdance.com>wrote:

On 04/12/2013 07:51 AM, Barry Warsaw wrote:
Huh?? From the PEP ============ The ``Enum`` class supports iteration. Iteration is defined as the sorted order of the item values:: >>> class FiveColors(Enum): ... pink = 4 ... cyan = 5 ... green = 2 ... blue = 3 ... red = 1 >>> [v.name for v in FiveColors] ['red', 'green', 'blue', 'pink', 'cyan'] -- ~Ethan~

On Apr 12, 2013, at 09:58 AM, R. David Murray wrote:
Nick brought this up in private email, and my response was basically that iteration order for Enums should not be guaranteed, even if that happens to work in the current implementation. The reason why it works in the current implementation is purely to provide predictable reprs. Of course, we could sort the values for the repr and remove the sorted() call in EnumMetaclass.__iter__() but then we would have to add it back for IntEnums, since we *do* want to make explicit iteration order guarantees for IntEnums, as they also have full rich comparisons. I'm just not sure it's worth it from an implementation point of view. I will update the PEP to make this more clear.
Why Enums and not IntEnums? Enums will not have ordered comparisons, but IntEnums will. -Barry

On Fri, 12 Apr 2013 10:50:44 -0400, Barry Warsaw <barry@python.org> wrote:
OK.
To take advantage of their incommensurability with other Enums. It's not a big deal, though; I'm more concerned that the API be internally consistent. I presume that one could always define an Enum subclass and provide comparison methods if desired :) --David

1) Is there limitation that EnumValues themselves must be immutable? 2) I'm most excited by the prospect of using these Enums as function defaults. I've not worked it through fully, but I'm guessing the following will work?
class Colors(Enum): ... red = (256, 0, 0)
3) Enums are generally used for defining constants - Is there a case to be made for using capitals in the 435 as PEP8 suggests, or are enums a special case? (http://www.python.org/dev/peps/pep-0008/#constants) 4) Is there an easy way to create custom EnumValues subclasses? In particular it'd be nice to be able to change the __repr__ in some cases to hide the value itself, which is often not important. Sorry if this has already been discussed elsewhere (I've only recently signed up to the dev mailinglist). On 12 April 2013 16:23, R. David Murray <rdmurray@bitdance.com> wrote:

On Apr 12, 2013, at 04:57 PM, Phil Elson wrote:
1) Is there limitation that EnumValues themselves must be immutable?
I'm not sure it makes sense to have mutable EnumValues, but yes, they are immutable in the sense that you cannot change their underlying value (well, without hackery).
Sure, why not? :) >>> from flufl.enum import Enum >>> X = Enum('X', 'a b c') >>> def doit(default=X.b): ... pass ... >>> from inspect import signature >>> print(signature(doit)) (default=<EnumValue: X.b [value=2]>)
I dunno, I like that the PEP isn't shouting at me. :)
Yes, although we aren't documenting it so we don't get locked into the API. If you look at the flufl.enum implementation, you'll see this is how we implement IntEnums. -Barry

Is there a convenient way to change this behavior, namely to indicate that conflicts are acceptable in a given Enum? While I like the idea of catching mistaken collisions, I've seen far too many C/C++ scenarios where multiple names map to the same value. This does raise questions, as it's unclear whether each name should get its own representation, or whether it's better to let DupEnum.name1 is DupEnum.name2 be True. (For the latter behavior, would adding DupEnum.name2 = DupEnum.name1 after the class declaration work today?) Michael -- Michael Urman

On Apr 12, 2013, at 09:03 AM, Michael Urman wrote:
Currently there is not. This behavior is defined in the metaclass.
(For the latter behavior, would adding DupEnum.name2 = DupEnum.name1 after the class declaration work today?)
Yes, but the repr/str of the alias will show the original value. >>> from flufl.enum import Enum >>> Colors = Enum('Colors', 'red green blue') >>> Colors.black = Colors.blue >>> Colors.black <EnumValue: Colors.blue [value=3]> >>> Colors.black is Colors.blue True -Barry

On Fri, Apr 12, 2013 at 9:30 AM, Barry Warsaw <barry@python.org> wrote:
That satisfies my concern. This gives an author the means to provide two names for a single value, and a way to choose which one is canonical. It's easy to imagine some corner cases related to persisting those values and then retrieving them with a later enum definition that changes the canonical name, but if you store raw values or names it should be easy enough to work around such things. Michael

On 12.04.13 15:55, Eli Bendersky wrote:
This is unnecessary because enumerations are iterable. Colors.__members__ is equal to [v.name for v in Colors] and the latter looks more preferable, because it does not use the magic method.
Does the enumeration's repr() use str() or repr() for the enumeration values? And same question for the enumeration's str().
How to get the enumeration value by its value?
It's unexpected if values of the enumeration values have the natural order. And values of the enumeration values *should be* comparable ("Iteration is defined as the sorted order of the item values").
Enumeration values ------------------
There is some ambiguity in the term "enumeration values". On the one hand, it's the singleton instances of the enumeration class (Colors.red, Colors.gree, Colors.blue), and on the other hand it is their values (1, 2, 3).
But if the value *is* important, enumerations can have arbitrary values.
Should enumeration values be hashable? At least they should be comparable ("Iteration is defined as the sorted order of the item values").
What is ``isinstance(Shape.circle, int)``? Does PyLong_Check() return true for ``IntEnum`` values?
Enumerations created with the class syntax can also be pickled and unpickled::
This does not apply to marshalling, I suppose? Perhaps this is worth to mention explicitly. There may be some errors of incompatibility.
Why the enumeration starts from 1? It is not consistent with namedtuple, in which indices are zero-based, and I believe that in most practical cases the enumeration integer values are zero-based.
Use-cases in the standard library =================================
The Python standard library has many places where named integer constants used as bitmasks (i.e. os.O_CREAT | os.O_WRONLY | os.O_TRUNC, select.POLLIN | select.POLLPRI, re.IGNORECASE | re.ASCII). The proposed PEP is not applicable to these cases. Whether it is planned expansion of Enum or additional EnumSet class to aid in these cases?

On Sat, Apr 13, 2013 at 1:31 AM, Serhiy Storchaka <storchaka@gmail.com>wrote:
Right. Fixed (removed this part because it's redundant), thanks.
str
I've updated the PEP since then. It also shows how to use __getitem__ syntax to access by value.
I agree, but not sure how to resolve it. I hope it's clear enough from the context.
See long discussion previously in this thread.
Yes. IntEnumValue (the value class underlying IntEnum) subclasses int.
No special provision has been made for marshalling. The ``Enum`` class is callable, providing the following convenience API::
Why the enumeration starts from 1? It is not consistent with namedtuple, in
which indices are zero-based, and I believe that in most practical cases the enumeration integer values are zero-based.
I don't know if there was a special reason for this. Perhaps backwards compatibility with existing flufl.enum APIs. Barry may know more about this.
Use-cases in the standard library =================================
The Python standard library has many places where named integer constants
It is applicable, in the sense that os.O_CREAT etc can be IntEnum values. Their bitset operation results will be simple integers. It's not planned to add a special enum for this - this was ruled against during the Pycon discussions. Eli

OK, so I finally got tie to read the PEP. I like it, I really have missed Enums, this is awesome. That's all folks! //Lennart

On 13.04.13 15:43, Eli Bendersky wrote:
On Sat, Apr 13, 2013 at 1:31 AM, Serhiy Storchaka <storchaka@gmail.com>wrote:
On 12.04.13 15:55, Eli Bendersky wrote:
May be use "enumeration items" or "enumeration members" if instances of the enumeration class have in mind? And left "enumeration names" and "enumeration values" for sets of corresponding attributes (.name and .value) of instances.
I think this requirements (hashability and comparability (for repr() and iteration)) should be mentioned explicitly.
But IntEnum is useless in such cases because a resulting mask will be an integer an will lost its convenient printable representation. There is almost no benefit of IntEnum before int constant.

[Eli]
On Sat, Apr 13, 2013 at 7:30 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
If you really wanted that you could define an int subclass that does better and use that -- IntEnum is just one example of how you can override the value type of Enum. (And yes, I am now +1 on documenting this mechanism.) -- --Guido van Rossum (python.org/~guido)

On Apr 13, 2013, at 08:33 AM, Guido van Rossum wrote:
(And yes, I am now +1 on documenting this mechanism.)
Here's what I've added to the flufl.enum documentation: Customization protocol ====================== You can define your own enumeration value types by using the ``__value_factory__`` protocol. This is how the ``IntEnum`` type is defined. As an example, let's say you want to define a new type of enumeration where the values were subclasses of ``str``. First, define your enumeration value subclass. >>> from flufl.enum import EnumValue >>> class StrEnumValue(str, EnumValue): ... def __new__(cls, enum, value, attr): ... return super(StrEnumValue, cls).__new__(cls, value) And then define your enumeration class. You must set the class attribute ``__value_factory__`` to the class of the values you want to create. >>> class StrEnum(Enum): ... __value_factory__ = StrEnumValue Now, when you define your enumerations, the values will be ``str`` subclasses. :: >>> class Noises(StrEnum): ... dog = 'bark' ... cat = 'meow' ... cow = 'moo' >>> isinstance(Noises.cow, str) True -Barry

On Apr 13, 2013, at 11:31 AM, Serhiy Storchaka wrote:
__members__ was really a holdover from earlier versions. I've removed this, but I've added an __dir__().
No, enumeration values have different reprs and strs.
And same question for the enumeration's str().
Enumerations share a repr and str (well, technically, Enums don't define a __str__()).
Use getitem syntax: >>> from flufl.enum import Enum >>> A = Enum('A', 'a b c') >>> A[2] <EnumValue: A.b [value=2]>
This is one reason why Enums are not comparable except by equality. While I think it's not a good idea to mix the types of Enum item values, it *is* possible, and I don't think we should add strict checks to enforce this for the base Enum. Thus we cannot guarantee that <, >, <=, or >= will not throw a TypeError. IntEnums do define these because they can be guaranteed to succeed, since their enumeration item values are guaranteed to be integers.
I've just made sure that the flufl.enum using.rst document is consistent here. The terms I'm using are "enumeration item" to define things like Colors.red and "enumeration item value" (or sometimes just "enumeration value") to define the value of the enumeration item, e.g. 2, and it's available on the .value attribute of the item. "Enumeration item name" is essentially the attribute name, and is available on the .name attribute of the item.
Given my previous responses, these questions should be already answered.
True. Yes, because IntEnumValues inherit from int.
There are several reasons: * It's been that way since day 1 <wink> of the package * Zero is special in a way; it's the only false integer value * This is very easy to override * If you're using the auto-numbering convenience API, then you don't care about the values anyway
IntEnums work fine for these cases, but it's true that the result of a logical operation is an int and not a subclass with a nice repr. Contributions are welcome. -Barry

Thank you for your answers, Barry. Eli already answered me most of my questions. 20.04.13 22:18, Barry Warsaw написав(ла):
Yes, values can have different reprs and strs (but ints haven't). What of them uses repr of an enumeration item? I.e. what is str(Noises): '<Noises {dog: bark}>' or '<Noises {dog: "bark"}>'? class Noises(Enum) dog = 'bark' flufl.enum uses str(), but is it intentional? If yes, than it should be specified in the PEP.
Eli and you have missed my first question. Should enumeration values be hashable? If yes (flufl.enum requires hashability), then this should be specified in the PEP. If no, then how you implements __getitem__? You can use binary search (but values can be noncomparable) or linear search which is not efficient.

Hello, Le Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> a écrit :
I'm having a problem with the proposed implementation. I haven't found any mention of it, so apologies if this has already been discussed:

Hello, (sorry for the previous message attempt - my mouse pointer hit the send button before I was finished with it) Le Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> a écrit :
I'm having a problem with the proposed implementation. I haven't found any mention of it, so apologies if this has already been discussed:
It would really be better if instances were actual instances of the class, IMO. Regards Antoine.

On Tue, Apr 23, 2013 at 11:44 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Looking at the source (https://bazaar.launchpad.net/~barry/flufl.enum/trunk/view/head:/flufl/enum/_...), I'm not seeing any fundamental technical issues with merging the Enum and EnumValue class definitions, and then using "cls" where the metaclass code currently uses "cls.__value_factory__" (even for the backwards compatible variant, the 2v3 metaclass issue isn't a problem, you can just define a _BaseEnum class with the right metaclass using the 2 & 3 compatible notation and inherit from that in a normal class definition) However, there's one non-technical aspect of such a merger which does concern me, which is the fact that you would lose the distinct docstrings for the class and the values:
EnumValue('Color', 'red', 12) prints as 'Color.red' and can be converted to the integer 12. So I'm not sure the PEP has made the wrong choice here, but I agree the point is at least worth mentioning. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Le Wed, 24 Apr 2013 00:22:40 +1000, Nick Coghlan <ncoghlan@gmail.com> a écrit :
You can work it around by making __doc__ a descriptor that behaves differently when called on a class or an instance. There is a slight metaclass complication because __doc__ is not writable on a class (but I suppose enums are already using a metaclass, so it's not much of an issue): class docwrapper: def __init__(self, class_doc, instance_doc_func): self.class_doc = class_doc self.instance_doc_func = instance_doc_func def __get__(self, instance, owner): if instance is None: return self.class_doc else: return self.instance_doc_func(instance) def instancedocwrapper(instance_doc_func): class metaclass(type): def __new__(meta, name, bases, dct): dct['__doc__'] = docwrapper(dct['__doc__'], instance_doc_func) return type.__new__(meta, name, bases, dct) return metaclass class D(metaclass=instancedocwrapper( lambda self: "My instance:{}".format(self.x))): """My beautiful, documented class.""" def __init__(self, x): self.x = x class E(D): """My magnificent subclass.""" print("class doc:", D.__doc__) print("subclass doc:", E.__doc__) print("instance doc:", E(5).__doc__) Note that the builtin help() function always displays the class's __doc__, even when called on an instance which has its own __doc__. Regards Antoine.

I gotta say, I'm with Antoine here. It's pretty natural (also coming from other languages) to assume that the class used to define the enums is also the type of the enum values. Certainly this is how it works in Java and C++, and I would say it's the same in Pascal and probably most other languages. This doesn't seem to matter much today, but various people are thinking about adding optional type annotations to Python code (use cases include hints for liters and better suggestions for IDEs). What would be more natural than being able to write class Color(Enum): RED = 1 WHITE = 2 BLUE = 3 ORANGE = 4 class Bikeshed: def paint(self, hue: Color, opacity: int): ... It would be awkward if the 'how' parameter had to be annotated with Color.ValueClass, or if the type checkers had to learn to imply .ValueClass if an attribute was labeled with an Enum type. Enums just aren't special enough. I find the docstring issue secondary. --Guido On Tue, Apr 23, 2013 at 7:57 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
-- --Guido van Rossum (python.org/~guido)

On Tue, 23 Apr 2013 08:11:06 -0700, Guido van Rossum <guido@python.org> wrote:
Well, I guess I can wrap my head around it :) An Enum is an odd duck anyway, which I suppose is one of the things that makes it worth adding. --David

On Tue, Apr 23, 2013 at 8:31 AM, R. David Murray <rdmurray@bitdance.com> wrote:
Sorry, you're being to literary/poetic. I can't tell what you meant by this response. -- --Guido van Rossum (python.org/~guido)

On Tue, 23 Apr 2013 08:44:21 -0700, Guido van Rossum <guido@python.org> wrote:
I was alluding to the fact that in Python we usually work with instances of classes (not always, I know, but still...), but with Enum we are really using the class as a first level object. Given that, breaking my (questionable?) intuition about isinstance should not be unexpected. And by that last phrase I meant to refer to the fact that getting this right is obviously non-trivial, which is one of the things that makes it worth adding as a Python feature. --David

On Apr 23, 2013, at 8:11 AM, Guido van Rossum wrote:
Furthermore, you could define methods/fields on enum values, like Java's enums. E.g.: Errno.EBADF.strerror() -> 'Bad file descriptor' Easily adapt to additional fields: class Protocol(Enum): HOPOPT = 0, "IPv6 Hop-by-Hop Option" ICMP = 1, "Internet Control Message" IGMP = 2, "Internet Group Management" @property def value(self): return self._value[0] @property def description(self): return self._value[1] -- Philip Jenvey

On Apr 23, 2013, at 01:20 PM, Philip Jenvey wrote:
Furthermore, you could define methods/fields on enum values, like Java's enums.
There's no reason why you couldn't do this now, even with the class separation. I've documented the customization protocol, so just add these methods to the class you set in __value_factory__. No need to conflate the classes, which I think just adds unnecessary complexity and confusion. -Barry

On Tue, 23 Apr 2013 15:44:58 +0200, Antoine Pitrou <solipsis@pitrou.net> wrote:
The first False looks correct to me, I would not expect an enum value to be an instance of the class that holds it as an attribute. The second certainly looks odd, but what does it even mean to have an instance of an Enum class? --David

Le Tue, 23 Apr 2013 10:24:18 -0400, "R. David Murray" <rdmurray@bitdance.com> a écrit :
Perhaps I should have added some context:
It is simply the same as a __getattr__ call. That said, I don't see why it wouldn't make sense for an enum value to be an instance of that class. It can be useful to write `isinstance(value, MyEnumClass)`. Also, any debug facility which has a preference for writing out class names would produce a better output than the generic "EnumValue". Regards Antoine.

On Tue, 23 Apr 2013 16:33:02 +0200, Antoine Pitrou <solipsis@pitrou.net> wrote:
Ah. I'd be looking for a bug every time I saw isinstance(value, myEnumClass). A better class name for values and an API for getting that class from the EnumClass would be nice, though. (So that you could write "isinstance(value, MyEnumClass.ValueClass)", say.) --David

On 4/23/2013 10:53 AM, R. David Murray wrote:
Reading what you have wrote, it seems that the issue is whether you consider an instance of Enum a "thing" or a "class of things". If you think of it as a "thing", then "C" is a object that has attributes for other "things" that are not like "C". However, if you think of "C" as a "class of things", then "C" having attributes that are instances of it's type is completely natural. Fundamentally, the question is whether an instance of Enum is a new type or an instance. And for me, it's a new type and I expect enum values to be instance of that type. -Scott -- Scott Dial scott@scottdial.com

On 4/23/2013 11:58 AM, Guido van Rossum wrote:
You seem to be mixing up classes and metaclasses.
I was trying to explain it in more plain terms, but maybe I made it even more confusing.. Anyways, I agree with you that isinstance(Color.RED, Color) should be True. -- Scott Dial scott@scottdial.com

On Apr 23, 2013, at 10:53 AM, R. David Murray wrote:
I think if we did this, the attribute should be the same name as the one used to customize the value factory. Okay, this is horrible, but you could use isinstance(value, MyEnumClass.__value_factory__) The only thing stopping you from doing this right now is that when __value_factory__ is not given, a default is used which is not available on that attribute. That's easily corrected though. 1) really, to be consistent with the documentation, this should be __item_factory__ since the attributes of the enum class are called "items", while items have an underlying value (e.g. A.b.value). 2) A better name than either __item_factory__ or __value_factory__ is welcome, though I guess that will spawn another bikeshedding, soul-destroying centi-thread. ;) 3) I'd like to make the implementation names consistent with the documentation in the next version of flufl.enum. -Barry

On Apr 23, 2013, at 04:33 PM, Antoine Pitrou wrote:
These semantics seem very weird to me, but at least we have a principled way to lie about it in Python 3. We could add this to the metaclass: def __instancecheck__(cls, instance): return instance.enum is cls or cls in instance.enum.__bases__ Thus: >>> X = Enum('X', 'a b c') >>> Y = Enum('Y', 'z y x') >>> class Z(Y): ... d = 4 ... e = 5 ... >>> isinstance(Z.d, Y) True >>> isinstance(Z.d, Z) True >>> isinstance(Z.d, X) False >>> isinstance(Y.z, Y) True >>> isinstance(Y.z, Z) False -Barry

R. David Murray wrote:
This attitude baffles me. In most other languages having a notion of an enum, when you define an enum, you're defining a type. The name of the enum is the name of the type, and its values are instances of that type. Why should our enums be any different? -- Greg

On Wed, 24 Apr 2013 11:37:16 +1200, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Obviously they don't need to be, since people have discussed how to implement this. But I am primarily a Python programmer, so my intuition is based on my Python experience, not on what other languages do. Given: MyEnum(Enum): a = 1 b = 2 I understood that 'a' and 'b' are class attributes, where the int value had been transformed into instances of a (separate) value class rather than being ints. The fact that there was a specific value class had been discussed. If 'a' is now an instance of MyEnum, then I would expect that: MyEnum.a.b would be valid (b being an attribute of the MyEnum class which should therefore be accessible from an instance of that class). That seems a bit odd, and based on my Python-only mindset, I saw no particular reason why an enum value *should* be instance of the enum class, since it would lead to that oddity. (Well, I'm not sure I was concious of that *particular* oddity, but in general it seemed like an odd thing to have class attributes of a class be instances of that class when the set of class attributes was the most important thing about that class...). It seemed more natural for the values to be instances of a separate value class. Now, given that I was viewing the Enum as being a collection of attributes whose values were instances of a different class, what would it mean to create an instance of the Enum class itself? You'd have an instance with access to those class attributes...but the getitem wouldn't work, because that's on the metaclass. You'd really want the Enum class to be a singleton...the important thing was that it was an instance of the metaclass, its instances would be pretty useless. I don't have any *problem* with enum values being instances of the class. If you make the attribute values instances of the enum class, then yes instances of enum class have a meaning. And then having attributes of the class be instances of the class makes perfect sense in hindsight. It's just not where *my* Python-only intuition, or my understanding of the discussion, led me. I feel like I'm revealing my ignorance or stupidity here, but what the heck, that's what was going through my brain and I might as well own up to it :). --David

On Tue, Apr 23, 2013 at 7:12 PM, R. David Murray <rdmurray@bitdance.com> wrote:
I'm not too worried. Clearly the Enum base class is magic, because, contrary to the behavior of regular class definitions, MyEnum.a is *not* just the integer 1 (even if it compares == to 1). At that point we may as well accept that the entire construct is magic, and the only consequences we can reason about are those that are documented as consequences of using this particular kind of magic. I'm not sure that we can implement a hack to prevent MyEnum.a.b from being an alias for MyEnum.b, but even if we cannot prevent that, I don't think we need to worry about it. (However, I suspect we can override __getattr__ to prevent this if it is considered too unsavory.) -- --Guido van Rossum (python.org/~guido)

R. David Murray wrote:
That is indeed a quirk, but it's not unprecedented. Exactly the same thing happens in Java. This compiles and runs: enum Foo { a, b } public class Main { public static void main(String[] args) { System.out.printf("%s\n", Foo.a.b); } } There probably isn't much use for that behaviour, but on the other hand, it's probably not worth going out of our way to prevent it. -- Greg

On Apr 23, 2013, at 10:24 AM, R. David Murray wrote:
Agreed, completely.
The second certainly looks odd, but what does it even mean to have an instance of an Enum class?
It only looks odd because it's using failed, duplicate, deprecated syntax. Does this look similarly odd?
isinstance(C[1], C) False
given that ``C[1] is C.a``? -Barry

On Thu, Apr 25, 2013 at 11:50 AM, Barry Warsaw <barry@python.org> wrote:
I don't know what's going on, but it feels like we had this same discussion a week ago, and I still disagree. Disregarding, the C[i] notation, I feel quite strongly that in the following example: class Color(Enum): red = 1 white = 2 blue = 3 orange = 4 the values Color.red etc. should be instances of Color. This is how things work in all other languages that I am aware of that let you define enums. -- --Guido van Rossum (python.org/~guido)

On Apr 25, 2013, at 02:54 PM, Guido van Rossum wrote:
I don't know what's going on,
Mostly that this is my first opportunity to chime in on the subject.
Is it enough that isinstance(Color.red, Color) returns True, or do you really-and-truly want them to be instances of Color? I still think it's weird, but I could accept the former if you're flexible on the latter, which in some sense is just an implementation detail anyway. -Barry

On Thu, Apr 25, 2013 at 3:02 PM, Barry Warsaw <barry@python.org> wrote:
Clearly this is a trick question. :-) I was told when this was brought up previously (a week ago?) that it would be simple to make it truly the same class. I suppose you were going to propose to use isinstance() overloading, but I honestly think that Color.red.__class__ should be the same object as Color. -- --Guido van Rossum (python.org/~guido)

On Apr 25, 2013, at 03:19 PM, Guido van Rossum wrote:
Clearly this is a trick question. :-)
A bit, yes. :)
I was told when this was brought up previously (a week ago?) that it would be simple to make it truly the same class.
It didn't sound simple to me, but I haven't seen any actual code yet.
Yes, a custom __instancecheck__() is two lines of code. I just can't get over the weirdness of a class having attributes which are actual instances of itself. -Barry

On Thu, Apr 25, 2013 at 3:29 PM, Barry Warsaw <barry@python.org> wrote:
TBH I had a hard time getting over the fact that even though the class said "a = 1", C.a is not the integer 1. But I did get over it. Hopefully you can get over *this* weirdness. -- --Guido van Rossum (python.org/~guido)

On 4/25/2013 3:37 PM, Guido van Rossum wrote:
:) The minute "metaclass" is involved, it is no longer a class, but a whatchamacallit. What blows people's minds regarding metaclasses is that they aren't necessarily what they look like. In this case, we are defining enumerations using a syntax that sort of looks like class attribute syntax, which are neither classes, nor types, but rather a collection of cooperative objects with shared methods in a shared subsidiary namespace. Perhaps instead of metaclass altering the behavior of classes, it might have been less confusing to simply define a new keyword "whatchamacallit" (or "metaclass") that have behaviors defined by the various methods defined in connection with it... while the existing techniques exist and would require a deprecation cycle to eliminate, it may not be too late to define alternate keywords that make it clearer that using such keywords, is making user-defined objects of some sort, class-like or -unlike, type-like or -unlike, object-like or -unlike, cooperative or not, etc., all by user definition. Any behavioral resemblance to classes would be only by user specification of same (perhaps such a specification of "same as class" should be easy). My question is, once an enumeration is defined, is there a way, short of element-by-element assignment, to import the individual enumeration instances into the current namespace, so that I can say "red" instead of "Color.red" ? I understand the benefits of avoiding name collisions when there are lots of enumerations, and lots of opportunities for name collections between, say, RGBColor and CYMKColor... but there are lots of uses for enumerations where the subsidiary namespace is just aggravating noise.

On 04/25/2013 04:26 PM, Glenn Linderman wrote:
You mean something like: --> class Color(Enum): ... RED = 1 ... GREEN = 2 ... BLUE = 3 --> Color.register() # puts Color in sys.modules --> from Color import * # doesn't work in a function, though :( --> BLUE Color.BLUE Yeah, that would be nice. ;) A bit dangerous, though -- what if another module does the same thing, but its Color is different? Better would be: --> Color.export(globals()) # put the enumerators in globals --> RED Color.RED -- ~Ethan~

On 26/04/13 11:53, Ethan Furman wrote:
It needs to put foo.Color in sys.modules, where foo is the name of the defining module. Then you do from foo.Color import * The drawback being that you need to write the name of the module into the import statement. It's disappointing that the import syntax doesn't have a way of saying "this module". -- Greg

On 4/25/2013 4:53 PM, Ethan Furman wrote:
Something like that, but that works in a function too :)
Globals? locals should be possible too. Or even something like: with Color: BLUE RED Although the extra indentation could also be annoying. One wouldn't want the module defining Color to automatically 'export' the colors: but rather a way to request an 'export' them into a particular scope. That way the proliferation of names into scopes is chosen by the programmer. import module_containing_color module_containing_color.Color.export_enumerations( globals ) or import module_containing_color module_containing_color.Color.export_enumerations( locals ) Or maybe locals is implicit, and in the file scope of a module, locals are globals anyway, so doing module_containing_color.Color.export_enumerations() would make the enumerations available to all definitions in the file, but inside a class or def doing the same thing would make the names direct members of the class or locals in the function.

On 04/25/2013 07:09 PM, Glenn Linderman wrote:
Not in Py3 it doesn't: Python 3.2.3 (default, Oct 19 2012, 19:53:16) [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. --> def test(): ... from sys import * ... print('huh') ... File "<stdin>", line 1 SyntaxError: import * only allowed at module level
At least in Cpython, updating locals() does not work in functions.
locals() can't be implicit, at least not without deep black magic of inspecting frames in the call stack -- which is hardly portable. -- ~Ethan~

On 4/25/2013 7:25 PM, Ethan Furman wrote:
Parse error. "Something like that, but something like that that works in a function too :)" is what I meant. I understand that the feature you demonstrated doesn't work in Py3... that's why we need "something like that" rather than "that" :)
So what I'm hearing is that enumerations need to be a language feature, rather than a module: Can't combine Enum and EnumItem Can't import into locals The compiler could do those things, though.

I wonder how many programmers will even notice that this characteristic exists. Exactly the same weirdness occurs in Java, but I had never thought about it until this discussion came up, and I wondered "Hmmm... seems like this ought to happen in Java too", tried it out, and found that it did. -- Greg

On Fri, Apr 26, 2013 at 8:29 AM, Barry Warsaw <barry@python.org> wrote:
I'm the one who said I didn't see any obvious barriers to the merger, but I've realised there is one, and it's similar to one namedtuple struggles with: how to handle method definitions. With the current flufl.enum design, if you want to give instances of a particular enum additional behaviour, you can use a custom item type and add the methods there. Simple and relatively obvious. With a merged design, it becomes *really* hard to give the instances custom behaviour, because the metaclass will somehow have to differentiate between namespace entries that are intended to be callables, and those which are intended to be instances of the enum. This is not an easy problem to solve. So, while I was initially in the "merge them" camp, I'm back to thinking the core architecture of flufl.enum is correct, but there may be some API adjustment to do, such as: 1. Lose the __getitem__ on the metaclass, and replace that with __call__ 2. Ensure that isinstance(MyEnum.item, MyEnum) returns True (even though it isn't really) 3. Inspired by namedtuple, move the current Enum constructor functionality to an Enum._make() API (implemented either as a class method in Enum or as an ordinary method on the metaclass) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Apr 25, 2013 at 7:13 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
It's really sad that this technical problem exists (and I realize what it is) -- because the obvious syntax (whose equivalent works in Java, BTW) *seems* so natural: class Color: red = 1 white = 2 blue = 3 orange = 4 def wave(self, n=1): for _ in range(n): print('Waving', self) a = Color.red a.wave() Color.orange.wave(3)
If the above syntax won't work, that isinstance() outcome isn't really important. :-( Can't we do some kind of callable check? There may be some weird decorators that won't work, but they aren't likely to be useful in this context.
-- --Guido van Rossum (python.org/~guido)

On Fri, Apr 26, 2013 at 12:38 PM, Guido van Rossum <guido@python.org> wrote:
Yeah, it may not be as tricky as I feared: adding "not callable(attr_val)" to the conditions for deciding whether or not to convert a class attribute to an instance of the enum would likely suffice to address the method problem. You couldn't create an enum of callables, but that would be a seriously weird thing to do anyway.... Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 4/25/2013 7:49 PM, Nick Coghlan wrote:
But aren't all classes callable? But you are referring to the initial values of the items, but still, why should those be restricted from being any general object? Not being _functions_ is probably OK, but I'm not sure how strong the distinction is between functions and classes, regarding being callable... So objects are only callable if the class contains a def __call__, but still, that seems quite restrictive.

On 4/25/2013 8:22 PM, Greg wrote:
Perhaps so, but an enumeration of objects whose class defines __call__ would not be so weird. Does your __methods__ cure this problem? It looked interesting to me, but I'm not familiar enough with metaclass to be sure of anything about it :)

On Thu, Apr 25, 2013 at 8:39 PM, Glenn Linderman <v+python@g.nevcal.com> wrote:
an enumeration of objects whose class defines __call__ would not be so weird.
Seriously? You'd complexificate the basic usage in order to cater for such an esoteric use case? The *only* use cases that matter at all for enum values are ints and strings, and even the latter could be considered a luxury when compared to other languages' enums. -- --Guido van Rossum (python.org/~guido)

On 4/25/2013 9:19 PM, Guido van Rossum wrote:
No, I'd look for a solution/implementation that doesn't divide objects into "plain" and "esoteric" cases. Py3 now treats everything as objects. So an enumeration should be able to deal with any object as a value.

On Fri, Apr 26, 2013 at 10:36 AM, Guido van Rossum <guido@python.org> wrote:
I feel that this thread has lost track of it long ago. Some time back in the Enum discussions (some 350 messages ago or so), there was a proposal to have this: class Color(Enum): RED, BLUE, GREEN By doing some crazy-cool shenanigans. Although the syntax is great, it was rejected on the basis of being too magic. The recent proposals of folding Enum and EnumValue into one, having class members be instances of the class they're members of while supporting a bunch of other Enum requirements also go off the rails in terms of complexity and magic. 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. Eli

On Fri, Apr 26, 2013 at 11:17 AM, Eli Bendersky <eliben@gmail.com> wrote:
Perhaps. The thread certainly has lost focus -- there are separate subthreads about the ordering of items when iterating over the enum class and about the unification of the Enum and EnumValue classes, and perhaps others.
FWIW, I didn't think the syntax was great. "Great" syntax would have been something like enum Color: RED, BLUE, GREEN but introducing a new keyword isn't justifiable.
Yet the goal of the proposal is conceptual simplification: one class instead of two. Having class members that are also class instances is a non-issue. The complexity of the proposal is an implementation issue: we'd like to be able to define methods for the enum values, and the simplest way (for the user) to define methods for the enum values would be to allow def statements, possibly decorated, in the class. But now the implementation has to draw a somewhat murky line between which definitions in the class should be interpreted as enum value definitions, and which should be interpreted as method definitions. If we had access to the syntax used for the definition, this would be simple: assignments define items, def statements define methods. But at run time we only see the final object resulting from the definition, which may not even be callable in the case of certain decorators. I am still optimistic that we can come up with a rule that works well enough in practice (and the Zen rule to which I was referring was, of course, "practicality beats purity").
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. -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 26, 2013 at 2:41 PM, Guido van Rossum <guido@python.org> wrote:
I agree, and thanks for the thoughtful reply to my half-rant ;-) More comments below.
But users are not exposed to two classes. And for us, implementors, keeping things separate is IMHO simplification. There's a conceptual difference between a value of an enumeration and a collection of such values. This difference helps, I think, make IntEnum possible relatively easily - we can actually have an enumerated value that is-a integer and can be used instead of integers in legacy code. This requirement will heap additional complexity on the alternative where the enum and enum value are unified into the same class.
My reference to Java was w.r.t. the static-typing-minded requirement that isinstance(Color.red, Color) == True. I don't think that 99% of use-cases will care about the type of Color.red, and those that do for obscure reasons can accept an obscure class (EnumValue-something) as long as they have something distinct to dispatch upon. Eli

Eli Bendersky wrote:
Not if you think of an enum as a type and a type as defining a set of values. From that point of view, the enum itself is already a collection of values, and introducing another object is creating an artificial distinction. -- Greg

On 04/26/2013 06:37 PM, Greg Ewing wrote:
I agree (FWIW ;). It seems to me that the closest existing Python data type is bool. bool is a type and has exactly two members, which are static/singleton/only created once. Enum is a metatype which we use to create a type with a fixed number of members which are static/singleton/only created once. The salient differences: with Enum we name the type and the members with Enum the members are also attributes of the type As a concrete example, consider: class WeekDay(Enum): SUNDAY = 1 MONDAY = 2 TUESDAY = 3 WEDNESDAY = 4 THURSDAY = 5 FRIDAY = 6 SATURDAY = 7 If we follow bool's example, then like True and False are of type(bool), TUESDAY should be of type(WeekDay). -- ~Ethan~

Guido van Rossum wrote:
Another way we could tell the difference is if the def statement used a different protocol for making bindings than assignment. Suppose a def statement used in a class body called __defitem__, if it exists, instead of __setitem__. Then the metaclass would be able to do different things for defs and assignments. -- Greg

On 4/26/2013 6:22 PM, Greg Ewing wrote:
Well, some assignments could be for non-enumeration items, once you start allowing EnumItem in the list. Some method of grouping enumeration items, or grouping non-enumeration items would solve the problem. class Color( Enum ): Enum.__enumerationItems__( red=1, green=2, blue=3, ) # other methods and assignments

On 04/26/2013 07:29 PM, Glenn Linderman wrote:
Or, if we go with the metaclass magic of re-using the class/type name (and who doesn't love metaclass magic??): class Color(Enum): red = Color(1) green = Color(2) blue = Color 3) look_ma_not_an_enum = 4 -- ~Ethan~

On 27/04/13 12:51, Ethan Furman wrote:
and from a later email:
I'm sorry, but all these suggestions are getting the API completely backwards by making the common case harder than the rare case. We're creating an Enum, right? So the *common case* is to populate it with enum values. 99% of the time, enumerated values will be all that we want from an enum. So that's the case that needs to be simple, not the rare case where you have a non enum value in an enum class. The common case (enum values in an Enum class) should be easy, and the rare cases (ordinary class-like attributes) possible. Explicit is better than implicit: if you want something to *not* be processed by the Enum metaclass, you have to explicitly mark it as special. Dunders excepted, because they *are* special enough to break the rules. Since dunders are reserved for Python, I'm happy with a rule that says that dunders cannot be set as enum values (at least not via the metaclass). Otherwise, everything inside an Enum class is treated as an enum value unless explicitly flagged as not. Here's a dirty hack that demonstrates what I'm talking about. class EnumValue: # Mock EnumValue class. def __new__(cls, name, obj): print("making enum {!s} from {!r}".format(name, obj)) return obj class MetaEnum(type): def __new__(meta, name, bases, namespace): cls = super().__new__(meta, name, bases, {}) for name, value in namespace.items(): if meta.isspecial(value): value = value.original elif not meta.isdunder(name): value = EnumValue(name, value) setattr(cls, name, value) return cls @staticmethod def isdunder(name): return name.startswith('__') and name.endswith('__') @staticmethod def isspecial(obj): return isinstance(obj, skip) class skip: def __init__(self, obj): self.original = obj class Example(metaclass=MetaEnum): red = 1 blue = 2 green = lambda: 'good lord, even functions can be enums!' def __init__(self, count=3): self.count = count food = skip('spam') @skip def spam(self): return self.count * self.food -- Steven

On 04/27/2013 11:45 AM, Steven D'Aprano wrote:
I should have made a better example. In production code, doc strings can be priceless, so encouraging them seems like a good idea: class Planet(IntEnum): MERCURY = enum(1, doc='closest planet to the sun (?)') VENUS = enum(2, doc='the one with two names') EARTH = enum(3, doc='home sweet home') random_value = 42 Of course, I think it would be even better if the name were 'Planet' instead of 'enum' as that would emphasize the fact that we are actually creating items of the enumeration inside the enumeration. Kind of a shorthand for: class Planet(IntEnum): def __init__(...): ... def blahblah(...): ... Planet.MERCURY = Planet(...) Planet.VENUS = Planet(...) Planet.EARTH = Planet(...) which is the way I've done it for other classes in a similar situation.
While I agree that the common case should be simple, I also disagree that everything (especially functions) should easily be an enumerated value; the nice thing about being explicit as to which are the values (using 'enum' for example) is that it can also be used to capture functions in the rare case where that's what is desired. -- ~Ethan~

On 04/27/2013 12:47 PM, Ethan Furman wrote:
Just a quick followup: It seems to me that the *most* common case will be a simple name mapping, in which case one can do: Planet = Enum._make('Planet', 'MERCURY VENUS EARTH') and be done with it. -- ~Ethan~

On Apr 27, 2013, at 2:57 PM, Guido van Rossum wrote:
Call me crazy, but might I suggest: class Planet(Enum, values='MERCURY VENUS EARTH'): """Planets of the Solar System""" I've always wanted something similar for namedtuples, such as: class Point(NamedTuple, field_names='x y z'): """Planet location with Sun as etc""" Especially when the common idiom for specifying namedtuples w/ docstrings looks similar but leaves a lot to be desired w/ the required duplication of the name: class Point(namedtuple('Point', 'x y z')): """Planet location with Sun as etc""" (Raymond's even endorsed the former): http://bugs.python.org/msg111722 -- Philip Jenvey

On 04/27/2013 04:07 PM, Philip Jenvey wrote:
Okay, you're crazy! ;) I must be too, 'cause I really like that suggestion. Works easily, simple metaclass (or simple addition to current metaclass). Very nice. -- ~Ethan~

On 04/27/2013 04:17 PM, Ethan Furman wrote:
Having said that, what does it look like for a longer enum? class Planet( Enum, names=''' MERCURY VENUS EARTH MARS SATURN JUPITER URANUS PLUTO ''', ): '''Planets of the Solar System''' Not sure I like that. Ah well. -- ~Ethan~

On Sat, Apr 27, 2013 at 5:10 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
The problem with this and similar proposals is that it puts things inside string quotes that belong outside them. -- --Guido van Rossum (python.org/~guido)

Steven D'Aprano <steve@pearwood.info> writes:
However, without knowing that the MetaEnum metaclass will do some magic here, there's no way to know that there's anything special about red, blue and green. So I think there's actually a lot of implicit stuff happening here. In contrast, class Example(metaclass=MetaEnum): red = EnumValue(1) blue = EnumValue(2) green = EnumValue(lambda: 'good lord, even functions can be enums!') def __init__(self, count=3): self.count = count def spam(self): return self.count * self.food Makes it very clear that red, blue will not be attributes of type int, even if one has never heard of Enums or metaclasses before. I don't think this syntax is making the common case hard. By the same logic, you'd need to introduce C-style i++ postincrement because having just "i += x" makes the common case with x=1 "hard" as well. Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On Sat, Apr 27, 2013 at 7:41 AM, Guido van Rossum <guido@python.org> wrote:
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 "__value_factory__" 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@gmail.com | Brisbane, Australia

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@gmail.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On 04/27/2013 09:01 AM, Guido van Rossum wrote:
While this will certainly work, it means you can't have class variables that happen to be the same type as the enum -- so no int in an IntEnum, for example. The solution I like best is the helper class (called, originally enough, enum), and only those items get transformed: class Planet(IntEnum): MERCURY = enum(1) VENUS = enum(2) EARTH = enum(3) rough_pi = 3 # not transformed -- ~Ethan~

On Sat, Apr 27, 2013 at 10:04 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
If this means that the most plain vanilla enum definition still has to use the enum(i) notation, I'm against it. Why do you want rough_pi to be a class variable anyway? The whole point of an enum is that it's *not* a kitchen sink class. An enum for the planets will need other support code that doesn't live in the enum class -- it shouldn't be considered a general scope for miscellanea. (TBH, I think that using classes to scope variables is mostly misguided anyway -- the standard mechanism for scoping is the module.) -- --Guido van Rossum (python.org/~guido)

On 04/27/2013 10:35 AM, Guido van Rossum wrote:
The two primary use cases I see are the (1) quick give me some names to values so I can fiddle and experiment, and the (2) production code with nice docs and the whole shebang. For (1) I would just use the _make (or whatever it's called) to give me something; for (2) using 'enum()' so that a docstring can also be added (even encouraged ;) seems like a Good Thing. And no, I have no idea what rough_pi is doing there, besides being an example on an int that doesn't get transformed. ;) -- ~Ethan~

Guido van Rossum <guido@python.org> writes:
I think this is actually a big advantage. It makes it obvious that something special is going on without having to know that IntEnum uses a special metaclass. Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On 04/27/2013 07:12 PM, Greg Ewing wrote:
Overriding __init__ is a PITA because __init__ is also called when you do Planet(3) # get EARTH and __init__ was expecting a gravitational constant and radius (or something like that). A couple ways around that: 1) have the metaclass store the args somewhere special (e.g. _args), have __init__ look like `def __init__(self, value=None)`, and have the body treat _args as if it were *args 2) have a `_init` that the metaclass calls with the args instead of __init__ -- ~Ethan~

Ethan Furman wrote:
I don't much like either of those. It would be much nicer if one could just write an ordinary __init__ method and have it work as expected. It's possible to make it work, I think. The __call__ method of the metaclass is going to have to do something special anyway, so that Planet(3) can look up and return an existing instance instead of making a new one. And if it doesn't make a new instance, it's not going to call the __init__ method. -- Greg

On 04/27/2013 08:59 PM, Greg Ewing wrote:
Agreed, on both counts.
So far I've had that logic in __new__ (which, of course, has no control on whether __init__ is called); I'll check out __call__ as soon as I can. Thanks for the tip! -- ~Ethan~

On 04/27/2013 08:59 PM, Greg Ewing wrote:
It works beautifully! It's not even complicated because the metaclass __new__ uses object.__new__ to create the instances, so EnumType.__call__ is /only/ called in cases like Planet(3), or Planet('EARTH'). Sweet! -- ~Ethan~

On 04/26/2013 02:41 PM, Guido van Rossum wrote:
The rule I liked best is "ignore callables, descriptors, and anything with leading & trailing double underscores". Personally I'd modify that to simply "anything with two leading underscores" so you can have private variables. It seems Pythonic to me in that classes already treat all those things special. And if you want enums of any of those things you can instantiate & insert them by hand after the class definition. Does that fail in an important way? //arry/

On Sat, Apr 27, 2013 at 9:41 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Great, sounds like a plan. The exception for callables may not even be needed -- the callables we care about (and some non-callables, like properties) are all descriptors. Or do we care about nested class definitions? (The reason I'm not keen on a general exemption for callables is that some 3rd party objects you wouldn't necessarily expect to be callable actually are.) I agree on a general exemption for __dunder__ names. The problem with exempting __private is that by the time the metaclass sees them, they've already been mangled to _classname__private. And I could just about imagine a use case for having a private value in an enum. -- --Guido van Rossum (python.org/~guido)

On 04/26/2013 11:17 AM, Eli Bendersky wrote:
Although explicit reasons were not mentioned (and perhaps not even consciously recognized -- *sigh* someday I hope to be that good), there are very good ones beyond "being too magic" -- way too easy to introduce bugs: name lookup success looks exactly like name lookup failure, but the consequences were drastically different: class Color(Enum): BLACK RED GREEN BLUE class MoreColor(Enum): BLACK CYAN MAGENTA YELLOW BLACK in MoreColor is a bug that cannot easily be detected as it is a successful name lookup; the consequence is that CYAN, MAGENTA, and YELLOW are now off by one. Not being Dutch, I had to smack into that one before I gave up on the idea. -- ~Ethan~

On 26/04/13 13:22, Greg wrote:
I don't think iscallable will work, since that descriptors like staticmethod and classmethod aren't callable. Nor are properties. I think a solution may be an explicit decorator that tells the metaclass not to skip the object into an enum value: class Insect(enum.Enum): ant = 1 bee = 2 @enum.skip @classmethod def spam(cls, args): pass assert isinstance(Insect.spam, classmethod) One side effect of this is that now you can (ab)use the decorator to have regular data attributes. Whether that counts as a good thing or a bad thing, I leave up to others to decide... -- Steven

On 04/25/2013 10:01 PM, Steven D'Aprano wrote:
Another option is to check if the item is a descriptor (class, static, property, or other); yet another option is to check if the item is the type of the enum class (int for IntEnum, str for StrEnum, etc.). The code I posted earlier checks for callable and type -- checking for descriptor also would not be much more effort. -- ~Ethan~

On Fri, Apr 26, 2013 at 3:01 PM, Steven D'Aprano <steve@pearwood.info> wrote:
My point was there *are* broad categories that we can reasonably say "you can't use these as values in an enumeration". Callables are one, descriptors are probably another. Alternatively, we can flip it around and require that each enum definition nominate an expected value type (defaulting to int) and only convert class attributes that are instances of that type to instances of the enum class. Either can be made to work, so it's really Guido's call as to which he would prefer. As Barry noted, the next step is actually for someone to create a proof of concept that shows the merge is possible in practice, not just theory. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Steven D'Aprano wrote:
I don't think iscallable will work, since that descriptors like staticmethod and classmethod aren't callable. Nor are properties.
Hmmm, maybe we should look for a __get__ method as well? Enums of descriptors would seem to fall into the seriously-weird category as well. Or if, as Guido says, the only sensible things to use as enum values are ints and strings, just leave anything alone that isn't one of those. -- Greg

On 04/26/2013 12:34 AM, Greg Ewing wrote:
The standard Java documentation on enums: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html has an example enum of a "Planet", a small record type containing mass and radius--each of which are floats. I don't know whether or not it constitutes good programming, but I'd be crestfallen if Java enums were more expressive than Python enums ;-) FWIW I'm +0.5 on "the enum metaclass ignores callables and descriptors". This seems reasonably Pythonic, much more so than "ignore everything except ints and strings". And as long as we're special-casing it I think we should opt for flexibility. Certainly I see nothing wrong with enums of float, complex, Decimal, and Fraction, so I don't see a good place to draw the line with a whitelist. //arry/

On 04/26/2013 08:50 AM, Larry Hastings wrote:
A whitelist, if we go that route, should be on a per Enum basis -- so something like: class Planets(str, Enum): PLUTO = 'it is too a planet!' MERCURY = 'definitely a planet' SATURN = 'darn big planet' solar_mass = 82738273 # or some really big number light_year = 1827499 # another really big number (of feet!) def horrorscope(self, message): return ('%s will be in retrograde... ' 'don't communicate today.' % self) and only PLUTO, MERCURY, and SATURN would be converted to enums. -- ~Ethan~

26.04.13 18:50, Larry Hastings написав(ла):
This example requires more than features discussed here. It requires an enum constructor. class Planet(Enum): MERCURY = Planet(3.303e+23, 2.4397e6) VENUS = Planet(4.869e+24, 6.0518e6) EARTH = Planet(5.976e+24, 6.37814e6) MARS = Planet(6.421e+23, 3.3972e6) JUPITER = Planet(1.9e+27, 7.1492e7) SATURN = Planet(5.688e+26, 6.0268e7) URANUS = Planet(8.686e+25, 2.5559e7) NEPTUNE = Planet(1.024e+26, 2.4746e7) def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters @property def surfaceGravity(self): # universal gravitational constant (m3 kg-1 s-2) G = 6.67300E-11 return G * self.mass / (self.radius * self.radius) def surfaceWeight(self, otherMass): return otherMass * self.surfaceGravity This can't work because the name Planet in the class definition is not defined.

On 04/26/2013 09:27 AM, Serhiy Storchaka wrote:
The metaclass can define it easily: - have generic helper class (I call mine `attrs`) - __prepare__ creates an instance of the custom dict - __prepare__ then inserts the helper class into the custom dict with the same name as the (to be created) custom Enum type - return the custom dict to Python class is processed using custom dict - __new__ gets the custom dict back from Python - __new__ replaces all instances of the helper class with actual Enum instances (which it creates on the spot) - any other housekeeping necessary - __new__ returns the new Enum type, complete with all Enum instances Here's an example run: 8<----------planets.py------------------------------------------------ from aenum import Enum class Planet(Enum): MERCURY = Planet(3.303e+23, 2.4397e6) VENUS = Planet(4.869e+24, 6.0518e6) EARTH = Planet(5.976e+24, 6.37814e6) MARS = Planet(6.421e+23, 3.3972e6) JUPITER = Planet(1.9e+27, 7.1492e7) SATURN = Planet(5.688e+26, 6.0268e7) URANUS = Planet(8.686e+25, 2.5559e7) NEPTUNE = Planet(1.024e+26, 2.4746e7) def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters @property def surfaceGravity(self): # universal gravitational constant (m3 kg-1 s-2) G = 6.67300E-11 return G * self.mass / (self.radius * self.radius) def surfaceWeight(self, otherMass): return otherMass * self.surfaceGravity print(int(Planet.VENUS)) print(repr(Planet.VENUS)) print(Planet.VENUS.surfaceGravity) 8<----------planets.py------------------------------------------------ 8<----------actual run------------------------------------------------ 2 Planet('VENUS', 4.869e+24, 6051800.0, integer=2) 8.871391908774457 8<----------actual run------------------------------------------------ -- ~Ethan~

On 04/26/2013 09:27 AM, Serhiy Storchaka wrote:
It can't work because you inserted the word "Planet" there. If you omit the word "Planet", this would work fine with something like the metaclass instantiate-all-data-members behavior in flufl.enum 4. Here is the hack, demonstrated in Python 3: class Metaclass(type): def __new__(cls, name, bases, namespace): result = type.__new__(cls, name, bases, dict(namespace)) for name, value in namespace.items(): if not (callable(value) or name.startswith("__")): value = result(name, value) setattr(result, name, value) return result class Planet(metaclass=Metaclass): MERCURY = (3.303e+23, 2.4397e6) def __init__(self, name, value): self.mass, self.radius = value def surfaceGravity(self): return 6.67300E-11 * self.mass / (self.radius ** 2) def surfaceWeight(self, otherMass): return otherMass * self.surfaceGravity() print("If you weigh 175 pounds, on Mercury you'd weigh", Planet.MERCURY.surfaceWeight(175)) //arry/

Steven D'Aprano <steve@pearwood.info> writes:
In this case, wouldn't it be nicer to "decorate" those attributes that are meant to be enum values? I think having to use the class keyword to define something that really doesn't behave like an ordinary class is pretty confusing, and the following syntax would be a lot easier to understand at first sight: class Insect(enum.Enum): ant = enum.EnumValue(1) bee = enum.EnumValue(2) @classmethod def spam(cls, args): pass def ham(self, args): pass Obviously, EnumValue() would automatically assign a suitable number. Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On 26/04/2013 2:38 p.m., Guido van Rossum wrote:
Another possible solution: class Color: red = 1 white = 2 blue = 3 orange = 4 class __methods__: def wave(self, n=1): for _ in range(n): print('Waving', self) and have the metaclass pull the functions out of the __methods__ sub-object. -- Greg

On 04/25/2013 07:13 PM, Nick Coghlan wrote:
I'm probably going to regret asking this, but what's difficult with the following? 8<----------------------------------------------------------------------------------------- class EnumDict(dict): """ automatically assigns the next _int for an enum """ def __init__(yo): super().__init__() yo._allow_duplicates = False yo._value = 1 yo._base = 1 yo._enums = [] yo._type = None # object means enum, anything else means all must be of that type def __setitem__(yo, key, new_value): """ main purpose is to support auto-numbering of members """ existing = yo.get(key) if type(existing) is attrs: # attrs is a helper class raise TypeError('Attempted to reuse key: %s' % key) if not key[:2] == key[-2:] == '__': old_value = None if isinstance(new_value, attrs): old_value = new_value if new_value.integer is None: new_value = yo._value else: new_value = new_value.integer if not isinstance(new_value, int): raise TypeError( "an enum integer must be an instance of type <int>, not %s" % type(new_value) ) if not callable(new_value): # this if-else is probably not final if (isinstance(new_value, int) and old_value is not None or yo._type in (object, )): yo._check_duplicate_integer(new_value, key) yo._value = new_value yo._inc_integer() yo._enums.append(key) new_value = attrs(integer=new_value) elif yo._type is None: # random bunch of named constants yo._check_duplicate_integer(new_value, key) value = yo._value yo._inc_integer() yo._enums.append(key) new_value = attrs(value=new_value, integer=value) elif isinstance(new_value, yo._type): # single type of named constants if isinstance(yo._type, int): value = new_value else: value = yo._value yo._check_duplicate_integer(value, key) yo._inc_integer() yo._enums.append(key) new_value = attrs(value=new_value, integer=value) if old_value is not None: new_value, old_value.integer = old_value, new_value.integer dict.__setitem__(yo, key, new_value) def _inc_integer(yo): if yo._base == 1: yo._value += 1 else: if yo._value == 0: value = 1 else: value = floor(log(yo._value, 2)) value = 2 ** value value <<= 1 yo._value = value def _check_duplicate_integer(yo, new_value, key): for name, value in yo.items(): if not isinstance(value, attrs): continue if value.integer == new_value and name != key and not yo._allow_duplicates: raise TypeError('duplicate value for %s: %d' % (key, value)) 8<------------------------------------------------------------------------------------------- Basically, if the assigned value is not `attrs` or a literal of the same type as the enum, the metaclass ignores it. -- ~Ethan~

2013/4/26 Nick Coghlan <ncoghlan@gmail.com>
There is at least one more problem, enum inheritance, given: class Colors(Enum): red = 1 green = 2 blue = 3 class MoreColors(Color): cyan = 4 magenta = 5 yellow = 6 what type is MoreColors.red? -- 闇に隠れた黒い力 弱い心を操る

Piotr Duda wrote:
Given the implementation we're considering, it would probably be Colors. However, there's a worse problem with defining enum inheritance that way. The subtype relation for extensible enums works the opposite way to that of classes. To see this, imagine a function expecting something of type Colors. It knows what to do with red, green and blue, but not anything else. So you *can't* pass it something of type MoreColors, because not all values of type MoreColors are of type Colors. On the other hand, you *can* pass a value of type Colors to something expecting MoreColors, because every value of Colors is also in MoreColors. Moreover, suppose we have another type: class YetMoreColors(Colors): orange = 4 purple = 5 pink = 6 Now suppose a function expecting Colors gets an enum with the integer value 4. How should it be interpreted? Is it cyan or orange? What about if you write it to a database column and read it back? These considerations suggest to me that subclassing enums should be disallowed, or at least not officially supported. -- Greg

On 26/04/13 18:00, Greg Ewing wrote:
There are many places where Python demands an actual int, not a subclass. See the recent thread "Semantics of __int__, index". There's no reason why a function that expects a Color *must* accept subclasses as well. If it can, great. If it can't, document it and move on. It's not Color's responsibility to know everything about every subclass. Invert your thinking: the subclasses are in charge, not Color. Color can't be expected to give a value to 4. Only the subclass that defines it can. This is only a problem if you believe that subclassing == taxonomy hierarchy. It isn't. http://pyvideo.org/video/879/the-art-of-subclassing -- Steven

26.04.13 11:00, Greg Ewing написав(ла):
I propose do not use an inheritance for extending enums, but use an import. class Colors(Enum): red = 1 green = 2 blue = 3 class MoreColors(Enum): from Colors import * cyan = 4 magenta = 5 yellow = 6 An inheritance we can use to limit a type of values. class Colors(int, Enum): # only int values red = 1 green = 2 blue = 3 Colors.viridity = green

26.04.13 05:13, Nick Coghlan написав(ла):
What if use mixins? Shouldn't it work without magic? class ColorMethods: def wave(self, n=1): for _ in range(n): print('Waving', self) class Color(ColorMethods, Enum): red = 1 white = 2 blue = 3 orange = 4

On Apr 23, 2013, at 03:44 PM, Antoine Pitrou wrote:
Ignore the single argument call syntax for Enums please. As Eli pointed out, you have getitem syntax for this and the single argument call syntax is deprecated. It will be removed in a future version of flufl.enum and need not appear in stdlib enum. TOOWTDI. C.a and C[1] return the same object, and it seems perfectly natural to me that this object is *not* an instance of the enum class. In fact, it seems completely weird to me that C.a would be an instance of the enum class. It seems very rare that a class has attributes that are instances of that class. It's not even easy to do with traditional syntax. class Foo: a = Foo() b = Foo() c = Foo() Huh? -Barry

On Apr 25, 2013, at 01:18 PM, Ethan Furman wrote:
Not if you think of it as a lookup operation instead of an instantiation operation. It really is the former because neither syntax creates new enum item objects, it just returns an already existing one. -Barry

On Thu, Apr 25, 2013 at 2:17 PM, Barry Warsaw <barry@python.org> wrote:
I think it's important to stress what this syntax is actually going to be used for. No one (I hope) is actually going to write Animals(1) or Animals[1]. They will write Animals.ant - this is what enums are for in the first place! The way I see it, this syntax is for enabling *programmatic access* - if you pull the value from a DB and want to convert it to an actual enum value, etc. So do we really need to have two syntaxes for this? The call syntax already has other uses, and it's weird because: Enum(....) -> Creates new enums Animals(....) --> accesses values ?! This is contradictory Animals[...] to serve as a by-value lookup makes sense, though. Eli

On 04/25/2013 02:25 PM, Eli Bendersky wrote:
How about consistency? If I'm converting several types of items from a database I'd like to do something like: result = [] for field in row: type = get_type(field) # returns int, bool, str, or an Enum type result.append(type(field)) What you're suggesting means complicating the logic: result = [] for field in row: type = get_type(field) # returns int, bool, str, or an Enum type if isinstance(type, Enum): result.append(type[field]) else: result.append(type(field)) We just got NoneType fixed to actually return None instead of raising an error for this same type of scenario, why should we muddy it up again? -- ~Ethan~

On Thu, 25 Apr 2013 14:37:29 -0700, Ethan Furman <ethan@stoneleaf.us> wrote:
I haven't cared much about this particular bikeshed, but I find this a somewhat compelling argument. I'm working on a system that depends on exactly this standard behavior of (most?) built in types in Python: if you pass an instance or something that can be converted to an instance to the type constructor, you get back an instance. If Enums break that paradigm(*), someone would have to write a custom class that provided that behavior in order to use Enums with my system. I wouldn't say that was a show stopper, especially since my system may never go anywhere :), but it certainly is an exemplar of the issue Eli is talking about. --David (*) Hmm. NoneType(None) is still an error.

On 04/25/2013 03:46 PM, R. David Murray wrote:
Um, did you mean "of the issue Ethan is talking about"? 'Cause Eli is against it.
(*) Hmm. NoneType(None) is still an error.
Hmm, so it is. When I pushed for the change just having NoneType() work was sufficient. -- ~Ethan~

On Apr 25, 2013, at 02:25 PM, Eli Bendersky wrote:
Excellent point, and no, we don't :).
I think so too. :) Note that I discovered that the same two-value call syntax on Enum can be used on the derived classes. It's exactly the same as using subclassing syntax to extend an existing enum. E.g. >>> class A(Enum): ... a = 1 ... b = 2 ... c = 3 ... >>> class B(A): ... d = 4 ... e = 5 ... >>> B.a is A.a True >>> X = Enum('X', 'a b c') >>> Y = X('Y', (('d', 4), ('e', 5))) >>> Y.a is X.a True That's a nice symmetry. -Barry

On 04/25/2013 02:17 PM, Barry Warsaw wrote:
True, but I don't. ;) I think the closest comparable thing in Python is the boolean class; while True and False are not attributes of bool, they are the only two instances, and invoking bool is going to return one of the existing bool instances (which is to say, True or False). It's basically a conversion from whatever to bool. --> bool('something') # returns True --> bool(None) # returns False Similarly, an Enum converts a string or a number to it's comparable enumerator (right word, Stephen?) --> class Animal(Enum): ... ant = 1 ... bee = 2 ... fly = 3 --> Animal(2) # should return Animal.bee --> Animal('ant') # should return Animal.ant It seems to me that storing the instances on the class as attributes is mostly for convenience; we could just as easily not, and change the repr of enumerators to '<Animal> ant [int=1]'. -- ~Ethan~

Le Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> a écrit :
The PEP should mention how to get an enum from its raw value: >>> Colors[1] <EnumValue: Colors.red [value=1]> or: >>> Colors(1) <EnumValue: Colors.red [value=1]> It would perhaps be nice to have a .get() method that return None if the raw value is unknown: >>> Colors(42) ... ValueError: 42 >>> Colors.get(42) >>> Regards Anroine.

On Thu, Apr 25, 2013 at 2:42 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Yes, this is mentioned in the beginning of the same paragraph.
This syntax was considered initially but then rejected because it's confusing, and there already exists a way to lookup by value (Colors[1]). Eli

On Thu, Apr 25, 2013 at 5:29 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
It produces a deprecation warning in flufl.enum because flufl.enum specifically supported this earlier. It should not be supported in the stdlib implementation. The __call__ syntax has been repurposed for the convenience API:
The aforementioned deprecated syntax refers to __call__ with a single arguments (the convenience API by definition requires more than one). Eli

On Thu, Apr 25, 2013 at 8:46 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Because we already have a way to do that: Animals[1]. Why do you need two slightly different ways to do the same? Moreover, why do you want to make Animals.__call__ behave very differently based only on the number of args? This seems to be un-pythonic in multiple ways. Eli

On 04/25/2013 09:34 AM, Eli Bendersky wrote:
I think we're talking past each other (or I'm not awake yet ;). Animals is a class. Giving Animals a parameter (such as 1 or 'ant') should return the instance that matches. This is how classes work. I don't understand your assertion that there is another way to call Animals... do you mean something like: --> MoreAnimals = Animals('MoreAnimals', 'bird worm insect') ? -- ~Ethan~

On Thu, Apr 25, 2013 at 9:39 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Yes, this works in the current implementation. But I'm realizing that the recent proposals of making isinstance(Color.red, Color) True will turn things around anyway so this discussion may be moot. Eli

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 04/25/2013 12:39 PM, Ethan Furman wrote:
Animals is a class. Giving Animals a parameter (such as 1 or 'ant') should return the instance that matches.
Animals is *not* a class -- it just uses the class syntax as a convenient way to set up the names used to construct the new type. (This subtlety is why the metaclass hook is reputed to make peoples' brains explode).
This is how classes work.
Not really. Normal classes, when called, give you a new instance: they don't look up existing instances. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with undefined - http://www.enigmail.net/ iEYEARECAAYFAlF5fa4ACgkQ+gerLs4ltQ7FSwCgzhcoXonMO/7W+xYMpM4EvtTj nPIAnAkHtWxFMaU3dqfFUclNQkUcJ2FZ =C0/7 -----END PGP SIGNATURE-----

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On Apr 25, 2013, at 03:02 PM, Tres Seaver wrote:
Thanks for the great explanation! :) - -Barry -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAEBCAAGBQJReYSxAAoJEBJutWOnSwa/1a8QALXlY5GkrDTgjTNc+i3VFbpW nQ+5iYSUKk7E7A5HTE/GO1thG/3lVVLQpDvuTP+ETqHiVgZ/oKI0WFpfC7C6h85W U7qclwjfZ0kng529FscwNcICgPilHng/gOR4msatje59R9Sw54mDkIYDHllhYzdZ R1NeWzfBmKL9aZJ3xGaIKHEB6Cwwk6ZL0F7S87Voe9ipd6ihRPbJK4y574XPe/4i m8jIrEVHI/9KCiDEdJrMXX7u+61jLEmWW9UnMuDJkNItxg/oiVwknSd2munDh1Ti yf7dZPkfx4Dcarv3DH5K/D/wo8OaOiqnvrM0qTImQObCWk8RswnGT3/SJmBNsfmN gndc+UTcVDYsVHtr57SEc79Y6vEAgdOlkrOChgzEoXUh9DS2slYFGuVMajncRJTI RY14k2KzPy1FdPysADk3KW6BIZRvZHMvszNFcEQiMwrDv98zFPfCqsm1KME7eBSg 4fjD0e5f8V+yLCyeUMUZxB6KhFyPdDi53w9X2mOQx9TPJqOJPNS7kzgGcMxagLMI BEw+3L3c5B5FZSd9JIQeIm3r5Cfee9Dvfgcfd5Y3QsHzaGCSSermRXs0cqRS51Bl 4LT39RDm6E+rSguR8PSOabmhwUrfhr2KMzkfZXtR8RwStPH4Tii8zpiSgY88k9JD XUZvZxjFCwWl3syPcMNR =Eglp -----END PGP SIGNATURE-----

FWIW, If we're going ahead with the plan to make Colors.red an instance of Colors, maybe the class names used in examples should be singular, i.e. isinstance(Color.red, Color)? -- --Guido van Rossum (python.org/~guido)

On Apr 25, 2013, at 11:42 AM, Antoine Pitrou wrote:
No, this is a failed experiment. It's deprecated in flufl.enum and shouldn't make it into stdlib enum. getitem syntax is TOOWTDI.
class Methods(Enum): set = 1 get = 2 delete = 3 What is "Methods.get"? Catching the ValueError ``Methods[5]`` would raise doesn't seem so bad. -Barry

On Fri, Apr 12, 2013 at 2:55 PM, Eli Bendersky <eliben@gmail.com> wrote:
I like much of this PEP, but the exception type for this case seems odd to me. Wouldn't a TypeError be more appropriate here? Somewhat like this:
Cheers, Dirkjan

On Fri, 12 Apr 2013 11:02:54 -0400, Barry Warsaw <barry@python.org> wrote:
I think TypeError is more consistent with the rest of Python:
You get that automatically if you return NotImplemented from the comparison methods. I don't think you should be explicitly raising NotImplemented. --David

On Apr 12, 2013, at 11:29 AM, R. David Murray wrote:
Oh darn, this doesn't work for Python 2.7. You don't care for PEP 435, but flufl.enum will have to raise the TypeError explicitly for consistency. -Barry

On 04/12/2013 08:37 AM, Barry Warsaw wrote:
Yeah, 2.x has everything ordered; returning NotImplemented only works for the non-comparison methods (such as add, subtract, etc.); one of the nice things about 3.x. :) -- ~Ethan~

On 04/12/2013 08:02 AM, Barry Warsaw wrote:
NotImplemented makes it seem like we could implement it in a subclass -- is this true? Also, for a more direct comparison: --> 'a' < 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: str() < int() I would think this is the more appropriate exception and text to use. -- ~Ethan~

On Fri, Apr 12, 2013 at 8:02 AM, Barry Warsaw <barry@python.org> wrote:
Interesting. I'm having a hard time articulating why, but NotImplementedError just feels more right to me in this case.
To me, NotImplementedError means that a subclass didn't implement something it should have implemented. But that's not the case here, is it? It's not a bug in the class, it's a bug in the call site. So I agree it ought to be TypeError. -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 12, 2013 at 9:34 AM, Guido van Rossum <guido@python.org> wrote:
Seems I was late to this particular argument. :-) Anyway, as far as I can tell the PEP looks great. I personally think it would be nicer if regular Enums were ordered (as long as the underlying values are ordered), but I don't care enough to overrule the FLUFL. I do wonder about this passage in the PEP:
I don't understand what this has to do with storing enums in a database. But it reminded me that for the purpose of storing enums in a database, it would be nice to have two examples: one that stores the names and looks them up (do you really have to use getattr() for that?), and one that stores the values and looks them up (how do you do that at all?). Should the metaclass-based API used to create IntEnum be documented, so strongly motivated people can write their own crazy variants? -- --Guido van Rossum (python.org/~guido)

In article <CAP7+vJLUO0B0y+=Jcg8D=jQ3mggSAm1tb9ZzttO7nCrKE6Mh6Q@mail.gmail.com>, Guido van Rossum <guido@python.org> wrote:
I, too, would strongly prefer to see ordering within an enum. I use home-made enums heavily in my code and find ordering comparisons useful there. Using intEnum is certainly doable, but that opens up the door to comparing values from different Enums, which is not something I'd want to allow. I don't understand why order tests are seen as a bad thing, as long as the values have order (as they will in the common cases of string and int values). It seems the code must go out of its way to prohibit such tests. In any case, I'm very pleased to have this library. it will get rid of much boilerplate in my code. It seems very well designed, and I'm really glad to see it supports subclassing to add values! -- Russell

On Apr 12, 2013, at 11:21 AM, Russell E. Owen wrote:
This was all hashed out in gory detail on python-ideas. I feel strongly that base EnumValues should be unordered, especially because the underlying values can be of any type. What would you expect to happen in this case: class X(Enum): a = 1 b = 'hi' if X.a < myvalue < X.b: # whaa? I think for most use cases, IntEnums will fit the bill for those who want ordered comparisons, and it's also easy to subclass EnumValues to specialize the behavior (in fact, this is how IntEnums are implemented). So if you really want ordered-comparisons-with-untyped-enum-values, you can have them. :) -Barry

On Fri, 12 Apr 2013 15:33:02 -0400, Barry Warsaw <barry@python.org> wrote:
You are right, the problem of comparison of disparate types makes ordering a non-starter. But by the same token that means you are going to have to be consistent and give up on having a sorted iteration and a stable repr:
--David

On Fri, Apr 12, 2013 at 1:52 PM, R. David Murray <rdmurray@bitdance.com>wrote:
I actually think that having values with different types within a single Enum is conceptually wrong and should be disallowed at creation time. With enums, you either care or don't care about their actual value. If you don't care (the most common use case of an enum, IMHO), then no problem here. If you do care, then it's probably for very specific reasons most of which are solved by IntEnum. I can't imagine why someone would need differently typed values in a single enum - this just seems like a completely inappropriate use of an enum to me. Eli

On Fri, 12 Apr 2013 14:06:55 -0700, Eli Bendersky <eliben@gmail.com> wrote:
I'm sure someone will come up with one :) But seriously, even if you require all values to be of the same type, that doesn't solve the sorting problem:
Now, you could *further* require that the type of enum values be sortable....but that point you really have no excuse for not allowing enum values to be compared :) --David

So, pragmatically, if e and f are values of the same enum class, couldn't e <cmp> f (where <cmp> is any comparison operator) defer to e.value <cmp> f.value ? Or is the problem with <cmp> being == and e and f being different enum values with the same underlying value? But that's already iffy, isn't it? (Barry's favorite solution for serializing to a database wouldn't work either.) And they could still be distinguished by using 'is' instead of '=='. On Fri, Apr 12, 2013 at 2:17 PM, R. David Murray <rdmurray@bitdance.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On Apr 12, 2013, at 02:34 PM, Guido van Rossum wrote:
If I'm parsing that correctly, yes, I think we don't want to defer to the enum.value for the base enum type because unrelated enumeration values should not compare equal. E.g. class Colors(Enum): red = 1 blue = 2 green = 3 class Animals(Enum): ant = 1 bee = 2 cat = 3 In this case, Animals.bee != Colors.blue. Of course, they would be == if they derived from IntEnums. While I personally recommend and use identity to compare enum types, it seems to be difficult to convince other users to also do so. We could enforce this by not implementing __eq__ and __ne__, but it seems worse to disallow this than to make it work. E.g. if shape.color is Colors.red: # works if shape.color == Colors.red: # throws an exception -Barry

On Fri, Apr 12, 2013 at 3:44 PM, Barry Warsaw <barry@python.org> wrote:
No, my proposal was only meant for if the classes are the same. If the classes are different the comparison should always fail.
Right, I *only* meant this as a way to differentiate between bee and wasp in: class Insect(Enum): wasp = 1 bee = 1 ant = 2 We'd have Insect.wasp == Insect.bee < Insect.ant but Insect.wasp is not Insect.bee. -- --Guido van Rossum (python.org/~guido)

On 13/04/13 10:13, Glenn Linderman wrote:
can't define two names in the same enum to have the same value, per the PEP.
I think that's too strong a restriction. I would expect to be able to do this: class Insect(Enum): wsap = 1 # Oops, needed for backward compatibility, do not remove. wasp = 1 # Preferred. Use this in new code. bee = 2 ant = 3 Or at the very least: class Insect(Enum): wasp = wsap = 1 bee = 2 ant = 3 I googled on "C enum" and the very first hit includes a duplicate value: http://msdn.microsoft.com/en-AU/library/whbyts4t%28v=vs.80%29.aspx And two examples from asm-generic/errno.h: #define EWOULDBLOCK EAGAIN /* Operation would block */ #define EDEADLOCK EDEADLK What's the justification for this restriction? I have looked in the PEP, and didn't see one. -- Steven

On 4/12/2013 10:51 PM, Steven D'Aprano wrote:
That's actually even better of an example than you may have realized because historically EWOULDBLOCK != EAGAIN[1]. So, there very well may need to exist such code as: if <some hairy platform>: _EAGAIN = <X> _EWOULDBLOCK = <Y> else: _EAGAIN = _EWOULDBLOCK = <X> class Errno(Enum): EAGAIN = _EAGAIN EWOULDBLOCK = _EWOULDBLOCK I don't think it's all that uncommon that enum values that represent states of a system get merged or renamed over time, and this one is a great example of that. [1] http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Error-Codes.... -- Scott Dial scott@scottdial.com

On Apr 13, 2013, at 12:51 PM, Steven D'Aprano wrote:
If you allowed this, there would be no way to look up an enumeration item by value. This is necessary for e.g. storing the value in a database. If you know that the "insect" column is an INTEGER that represents an enumeration item of Insect, then you can just store the int value in the column. To reconstitute the actual enumeration item when you read the column back from the database, you need to be able to look up the item by value. Currently, you do this: >>> my_insect = Insect[database_value] but if the values are not unique, you have no way to reliably do it. I don't much like APIs which return sequences (either always or "on demand") or rely on definition order or some other arbitrary discrimination because I don't think any of those are practical in the real world. I also recall that duplication was a specific anti-feature in the previous python-ideas discussion. It was thought that this would allow for typos in the set of enumeration items to creep in. I don't see how you can reconcile these issues to allow for duplicate values. -Barry

On 21/04/13 05:42, Barry Warsaw wrote:
I agree that's a good example of a situation where the user might want unique values. But I don't agree that this is the responsibility of the Enum type itself. Enums are a mapping from name to value, just like dicts: d = {'wasp': 1, 'bee': 2, 'ant': 3} There are use-cases where we might want dicts to be 1:1 too, but we don't force that restriction on all dicts. Even if I want to reconstruct the key from the value, doesn't mean that everybody who uses dicts must be prohibited from using duplicate values. We don't even offer a guaranteed 1:1 mapping type in the standard library. Actual real world enums can and do frequently contain duplicate values, I've previously given examples of such. The ability to have two enums with the same value is in my opinion not just a Nice To Have but is a Must Have. With the ability to have duplicate values, enums are the One Obvious Way to do it. Without it, the decision process becomes more complex: does my application now, or will it ever, need duplicate values? If there's even a tiny chance it might need them in the future, I cannot risk getting stuck with an enum type that prohibits that. I would argue that it is the responsibility of enums to start with the least restrictions as is reasonable, and leave additional restrictions up to subclasses, rather than the other way around. (I'll just quietly mention the Liskov Substitution Principle here...) If some people need enums to have unique values, then enforcing that should be their responsibility, and not forced on all users whether they need that restriction or not. If there is enough demand for that, then perhaps the enum module could provide a standard mechanism for enforcing unique values, via a flag, or a subclass, say. I like the idea of a mixin: class Insect(UniqueValues, Enum): wasp = 1 bee = 2 ant = 3 But it should be off by default.
Neither do I. I would be perfectly happy for enums to raise ValueError in that case, and leave it up to the caller to either prevent duplicate values from occurring, or to deal with the exception in whichever way makes sense for their application.
Typos occur whether enums allow duplicate values or not. How you deal with such typos depends on whether you are forced to keep it forever, or can define a second enum with the same value and deprecate the typo. -- Steven

On Sun, Apr 21, 2013 at 11:29 AM, Steven D'Aprano <steve@pearwood.info> wrote:
The PEP is fine, as it already allows duplicate names without encouraging them: class Insect(Enum): wasp = 1 # Preferred. Use this in new code. bee = 2 ant = 3 # Backwards compatibility aliases Insect.wsap = Insect.wasp If you have a lot of such aliases: aliases = { "wasp": ["wsap"], ... } for attr, names in aliases.items(): for name in names: setattr(Insect, name, getattr(Insect, attr)) A more concise syntax for handling duplicates may prove desirable at some point in the future, but this is a case where encouraging correctness by default is a good idea. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Barry Warsaw wrote:
On Apr 13, 2013, at 12:51 PM, Steven D'Aprano wrote:
Hm. What you really want there isn't two enum objects with the same value, but two names bound to the same enum object. Then looking it up by value would not be a problem. -- Greg

On 21 April 2013 21:02, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
If there were some way to identify the canonical name a lookup by value would be unambiguous. If we have iteration in definition order, I'd say the first defined name for a value should be the canonical name, and any other name for the value should be considered an alias. That would preclude the syntax above, but the following might be workable: class Insect(Enum): wasp = 1 bee = 2 ant = 3 # aliases wsap = wasp waps = 1 In the above, looking up by the value 1 would always return Insect.wasp. Tim Delaney

On 13.04.13 03:13, Glenn Linderman wrote:
For current flufl.enum implementations this requires values to be hashable. An alternative implementation can use comparability (which already required for repr() and iteration).

On Fri, Apr 12, 2013 at 2:17 PM, R. David Murray <rdmurray@bitdance.com>wrote:
Which is precisely the reason to ban it :)
I'm actually not really in favor of enum values being comparable. I think this is more a C-ism and does not cleanly settle with my concept of what an enum is. For comparable enums and other C-derived properties, IntEnum is out there, so call it maybe ;-) Eli

On Apr 12, 2013, at 05:17 PM, R. David Murray wrote:
I'd be more willing to give up on sorting for the base enum type's iteration and repr. It's not crucial functionality whereas I still don't want the base enum values to support ordered comparisons. -Barry

On 13 April 2013 08:32, Barry Warsaw <barry@python.org> wrote:
Just using definition order as the stable iteration order would do the trick - no need for any comparisons at all. Subclasses (e.g. IntEnum) can then override it. You could then easily have a subclass that implemented comparisons defined based on iteration order. It makes sense not to have this in the base Enum class (it would be confusing). On a related note, I really would like to have the ordinal exposed if this were added. Tim Delaney

On Apr 13, 2013, at 08:37 AM, Tim Delaney wrote:
I think this isn't possible if we want to keep backward compatibility with earlier Pythons, which I want to do. OTOH, we have another natural sorting order for base Enums sitting right in front of us: the attribute name. These have to be unique and ordered, so why not use this for both the __repr__() and the base Enum __iter__()? IntEnum can override __iter__() to iterate over item values, which also must be ordered. I just made this change to flufl.enum and it seems to work well.
-Barry

On Sat, 20 Apr 2013 14:10:32 -0400, Barry Warsaw <barry@python.org> wrote:
I think definition order would be much better, but if we can't have that, this is probably better than value order for non-int. --David

Can we separate the iteration order and the comparison order? For iteration order, I think by definition order or by attribute name are both great, and better than by value. But for comparing values using <, ==, >, I strongly feel we should defer to the underlying values -- if those cannot be compared, then the enums can't either, but the iteration order is still defined. On Sat, Apr 20, 2013 at 11:26 AM, R. David Murray <rdmurray@bitdance.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On 21 April 2013 04:10, Barry Warsaw <barry@python.org> wrote:
Do you want it compatible with Python 2.x? In that case I don't see a way to do it - getting definition order relies on __prepare__ returning an ordered dict, and __prepare__ of course is only available in 3.x. Tim Delaney

On Sun, Apr 21, 2013 at 9:10 AM, R. David Murray <rdmurray@bitdance.com> wrote:
Agreed. I think the stdlib enum library should use __prepare__ and iterate in definition order (since 2.x compatibility isn't of any concern), while flufl.enum can use "sorted by name" as the iteration order. An "order_by_name" keyword argument to __prepare__ in the stdlib version could then allow the user to opt in to the flufl.enum behaviour, while still using definition order by default. As in: class DefinitionOrder(enum.Enum): first = 1 second = 2 third = 3 list(DefinitionOrder) -> [DefinitionOrder.first, DefinitionOrder.second, DefinitionOrder.third] And: class NameOrder(enum.Enum, order_by_name=True): a = 1 c = 2 b = 3 list(NameOrder) -> [NameOrder.a, NameOrder.b, NameOrder.c] flufl.enum could also offer the "order_by_name" flag on 3.x, but set it to "True" by default. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Apr 21, 2013, at 03:25 PM, Nick Coghlan wrote:
Seriously, why all the extra complexity for something you'll never care about? You don't care about the exact contents of __repr__ as long as it's predictable. You don't really care about item iteration order either as long as it's defined. Sorting by attribute name fulfills both use cases *and* it's simple. Zen 3 FTW. -Barry

On Apr 20, 2013, at 07:10 PM, R. David Murray wrote:
It seems strange to limit a new Python3 feature to the Python2 feature set. Just saying :)
For a critical feature sure, but I don't put __repr__ or enum item iteration order in that category. There's no need for gratuitous incompatibility either, and attribute name order is just fine. -Barry

On 22 Apr 2013 07:50, "Barry Warsaw" <barry@python.org> wrote:
iteration
order in that category. There's no need for gratuitous incompatibility either, and attribute name order is just fine.
Iteration order matters a lot if you don't want people complaining about enums being broken: class Days(enum.Enum): Monday = 1 Tuesday = 2 Wednesday = 3 Thursday = 4 Friday = 5 Saturday = 6 Sunday = 7 Cheers, Nick.
http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com

On 22 April 2013 09:02, Nick Coghlan <ncoghlan@gmail.com> wrote:
I'm fine with iteration order being by sorted name by default, so long as it's easily overrideable by enum subclasses or metaclasses e.g. an IntEnum should probably iterate in value order. For definition order, a 3.x-only metaclass could be provided: class Days(enum.Enum, metaclass=enum.DefinitionOrder): Monday = 1 Tuesday = 2 Wednesday = 3 Thursday = 4 Friday = 5 Saturday = 6 Sunday = 7 Tim Delaney

On Apr 22, 2013, at 09:31 AM, Tim Delaney wrote:
It does</timemachine>. :)
Yep, that's how it works. From flufl.enum: class IntEnumMetaclass(EnumMetaclass): # Define an iteration over the integer values instead of the attribute # names. def __iter__(cls): for key in sorted(cls._enums): yield getattr(cls, cls._enums[key]) IntEnum = IntEnumMetaclass(str('IntEnum'), (Enum,), { '__doc__': 'A specialized enumeration with values that are also integers.', '__value_factory__': IntEnumValue, }) -Barry

On 22 April 2013 10:31, Barry Warsaw <barry@python.org> wrote:
I knew it *did*, but wasn't sure if with the current discussion it was going to continue to do so.
Would it be worthwhile storing a sorted version of the enum keys here? Or do you think the current space vs speed tradeoff is better? I need to grab the current flufl.enum code and see if I can easily extend it to do some more esoteric things that my enum implementation supports (*not* bare names, but maybe the name = ... syntax, which of course requires the definition order metaclass). I'm in the middle of a release cycle, so my time is somewhat limited right now :( Tim Delaney

On Sun, Apr 21, 2013 at 5:55 PM, Tim Delaney <timothy.c.delaney@gmail.com> wrote:
In any case I think it would be odd if IntEnum used a different policy than Enum. That would be a disturbing difference in behavior between two otherwise rather similar classes.
I'm convinced. I also think that 2/3 compatibility is not as important as getting it right for the foreseeable future. -- --Guido van Rossum (python.org/~guido)

On Apr 22, 2013, at 10:55 AM, Tim Delaney wrote:
Would it be worthwhile storing a sorted version of the enum keys here? Or do you think the current space vs speed tradeoff is better?
It's an implementation detail that doesn't bother me too much either way. -Barry

On Apr 22, 2013, at 09:02 AM, Nick Coghlan wrote:
Sorry, that's still not a complete use case. I don't see why you'd depend on iteration order over Days for any particular functionality. Besides, if you did, I think it would be better to derive Days from IntEnum and then iteration order is guaranteed over the values. -Barry

On Mon, Apr 22, 2013 at 10:28 AM, Barry Warsaw <barry@python.org> wrote:
You mean other than printing the days of the week in order without needing to worry about the specific values assigned to them? Using sort-by-name also introduces other weirdness, such as subclasses potentially inserting their values in the middle of inherited names, rather than appending to the end as one might reasonably expect. While using sort-by-name is better than not providing a consistent ordering at all, using definition order is substantially less surprising than sorting by key name, and PEP 3115 and collections.OrderedDict makes that easy to support in Python 3.x. The fact that this will make for a behavioural difference between the standard library and flufl.enum does *not* count as an argument for making the behaviour of the standard library version less intuitive (if that was a valid argument, the 3.3+ ipaddress module would look a *lot* more like it's ipaddr inspiration).
But only at the cost of breaking the other guarantees provided by a standard enumeration. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Apr 22, 2013, at 10:42 AM, Nick Coghlan wrote:
My point is, "days of the week" has a natural ordering, so why wouldn't you use IntEnum for that? Problem solved. There's no natural ordering for things like colors or animals, so the values don't matter. I claim that neither does the repr or iteration order except that the former should be *predictable* and it would be nice to define the latter, but that's not actually necessary. Undefined iteration order would be just as fine for Enum.
I don't see how iteration order could affect how you'd write the derived class syntax.
Maybe. If they care about iteration order at all, then they will have to special case their code for Python's < 3.3, which means they'll probably have to explicitly sort it anyway for the foreseeable future. I guess it'll be moot in 10 years though. ;) They probably don't care about iteration order, which I think will be the common case (heck, iteration over the enum will be pretty rare *anyway*). -Barry

On 04/25/2013 03:23 PM, Barry Warsaw wrote:
Because by using an IntEnum we lose the Enum type protection, which is one of the reasons to want an Enum type to begin with.
People like things sorted (or am I alone here?) There are three obvious natural orderings: 1 - value 2 - definition order 3 - name And that's my order of preference for them.
It probably wouldn't, but if I had: --> class Color(Enum): ... red = 1 ... blue = 2 ... green = 3 --> class MoreColor(Color): ... cyan = 4 ... magenta = 5 ... yellow = 6 I would be very unhappy with: --> list(MoreColor) [ MoreColor.blue, MoreColor.cyan, MoreColor.green, MoreColor.magenta, MoreColor.red, MoreColor.yellow, ] because 1) it's not the order I defined it in; and 2) it's not in value order. -- ~Ethan~

On 4/25/2013 3:23 PM, Barry Warsaw wrote:
My point is, "days of the week" has a natural ordering, so why wouldn't you use IntEnum for that? Problem solved.
While the ordering is natural, some implementations start from 0, some start from 1, and on the naming side, some start from Sunday, and some start from Monday. So there are lots of opportunities for the numbers vary, and having easy conversions to int which expose those numbers and allow comparisons to integers, is adding problems, not solving them. On 4/25/2013 3:36 PM, Ethan Furman wrote:
So, being blissfully unaware of the implementation issues, not having yet read the implementation, I suggest that the preferred iteration order should be a characteristic of the Enum when defined, and/or that there should be a method to obtain a list in any of the natural orderings (specifying value ordering may add a restriction that the values be orderable, if done at definition time; if done only on retrieval, attempting to access the list by value would raise an exception). Another possible ordering would be "random", and for enumerations with values that are tuples or lists or hashes, ordering by some specified element would be conceivable. On the other hand, except for "definition" order, all the other possible orderings could be derived externally to the enumeration. So if enumerations preserve/provide "definition" order, all the others could be implemented externally or as subtypes. "Definition" order, then is the basic ordering for enumerations because it cannot be externally derived, other than by reading the source code.

On 04/25/2013 06:03 PM, MRAB wrote:
Lots of counting systems wrap: seconds, minutes, hours, days of week, days of month, days of year, millimeters, inches, etc., etc., and so forth. We still apply ordering to them, and talk about 15 <whatevers> being less than 42 <whatevers>. -- ~Ethan~

On 26/04/2013 1:15 p.m., Ethan Furman wrote:
Lots of counting systems wrap: seconds, minutes, hours, days of week, days of month, days of year, millimeters, inches, etc., etc.
But we don't disagree on which is the first minute of an hour.
We still apply ordering to them, and talk about 15 <whatevers> being less than 42 <whatevers>.
When we do that, we're using cardinal numbers (how many), not ordinal numbers (what order). -- Greg

On 26/04/13 13:03, MRAB wrote:
But there _is_ an ordering problem, in that the days wrap around.
Do we want a CircularEnum, then? Ordering would be defined only up to the starting value, which you would be required to specify when doing anything where it mattered. class Day(CircularEnum): sunday = 0 monday = 1 ... saturday = 6 list(Day.startingat(Day.tuesday)) --> [Day.tuesday, Day,wednesday, Day.thursday, Day.friday, Day.saturday, Day.sunday, Day.monday] Modular arithmetic would apply, so Day.saturday + 3 --> Day.tuesday That would be the replacement for Day(3), which would be disallowed. -- Greg

On 26/04/2013 1:28 p.m., Ethan Furman wrote:
Interesting idea, but why does Day(3) have to be disallowed to make it work?
Because it's ambiguous. Which day of the week is number 3? It depends on where you start. I should perhaps point out that the numbers assigned to the values initially are just to establish the relative ordering. They wouldn't be directly accessible once the values are created. To get an integer value corresponding to a Day value, you would have to do arithmetic: Day.wednesday - Day.sunday --> 3 -- Greg

On Sun, 21 Apr 2013 20:28:16 -0400, Barry Warsaw <barry@python.org> wrote:
I didn't read Nick's message as being about a use case. The key word in the sentence above was "complaining" :) Regardless of the specific values involved, it is pretty much guaranteed that if anything other than definition order is used we *will* get bug reports/enhancement requests to fix it, on a regular basis. We can choose to live with that, but we should admit that it will will happen :) --David

On 04/25/2013 03:11 PM, Barry Warsaw wrote:
Whatever random order {}.keys() returns -- and python programmers should be expecting that (or at least smack themselves for forgetting it ;) -- ~Ethan~

On 13/04/13 05:33, Barry Warsaw wrote:
I would expect the same behaviour from enums that I get in Python 3 from non-enums. That is, if enums X.a and X.b happen to both be ints, or both strings, then comparisons should succeed, but if they are different types, I should get a TypeError. The above applies to related enums. If they are unrelated (e.g. Colours.red < Insects.ant) then I think TypeError is appropriate. -- Steven

On Apr 12, 2013, at 09:43 AM, Guido van Rossum wrote:
Not much, really. It's just hold over text from the original motivation for exposing the enum class as an attribute on the values. In Mailman, I store these values in my database and they get reconstituted correctly by the ORM layer. Anyway, in this particular case, I think the motivation is unnecessary for describing the API, so I'll remove that from the PEP.
It's going to be dependent on how you store and retrieve enum values. As an example, in my database layer I store the enum values in an integer column, with the ORM layer knowing which Enum subclass to use. So all I need to do to store the value is ``int(enum_value)`` and to get back the original enum value, I just do ``self._enum[int_value]`` where self._enum is the Enum subclass. To me, that's probably the most common way of doing it. If you store by name though, yes, you'd have to use ``getattr(self._enum, name)``. At one point Enums also supported getitem syntax for lookup by name, but consider this case: class Fruit(Enum): apple = 'red' banana = 'yellow' tangerine = 'orange' orange = 'reddish yellow' What should Fruit['orange'] return? In private email Nick pointed out that using getattr() for lookup by name works fine, and getitem for look up by value has been in the API since the beginning, so now Fruit['orange'] is documented to return Fruit.tangerine, i.e. lookup by value only. (Actually, in flufl.enum, lookup-by-name is still there but deprecated. We can just drop it for Python 3.4).
Should the metaclass-based API used to create IntEnum be documented, so strongly motivated people can write their own crazy variants?
I think you recommended against that in python-ideas :). -Barry

On Fri, Apr 12, 2013 at 12:26 PM, Barry Warsaw <barry@python.org> wrote:
Yeah, it looked like an editing mistake. :-)
Agreed. I can't easily find that in the PEP though. It doesn't mention __getitem__ and I can't find any examples of using <enumclass>[<int>].
Yeah, getattr() is good enough, and it is documented in the PEP.
I have changed my mind; I am now at least +0 on documenting the metaclass craziness. -- --Guido van Rossum (python.org/~guido)

On Apr 12, 2013, at 12:56 PM, Guido van Rossum wrote:
Agreed. I can't easily find that in the PEP though. It doesn't mention __getitem__ and I can't find any examples of using <enumclass>[<int>].
Indeed, this looks like an omission in the PEP. flufl.enum's usage documentation definitely talks about this: http://pythonhosted.org/flufl.enum/docs/using.html specifically: http://pythonhosted.org/flufl.enum/docs/using.html#conversions Eli, care to add this to the PEP?
It would be fine with me. I left it out of the flufl.enum docs and we left it out of the PEP after your original comments, but I'm pretty happy with the API and can't foresee us changing it (famous last words). FWIW, we use a special attribute called __value_factory__ on the Enum subclass to name the class used to create enum values. This is all there is to IntEnum: class IntEnum(Enum): """A specialized enumeration with values that are also integers.""" __value_factory__ = IntEnumValue and even the IntEnumValue class isn't that big. It can be even smaller in Python 3.4 because of the workarounds in flufl.enum for Python 2 compatibility, and deprecations. Eli, what do you think about documenting the extension API? Cheers, -Barry

On Fri, Apr 12, 2013 at 3:23 PM, Barry Warsaw <barry@python.org> wrote:
Done. getattr did get a mention there, but now I made it more prominent and described __getitem__ access as well.
I don't have major objections... Eli

Eli Bendersky <eliben@gmail.com> writes:
These enumeration values are not equal, nor do they and hence may exist in the same set, or as distinct keys in the same dictionary::
I'm not a native speaker and I found the above difficult to parse: is there anything missing between "nor do they" and "and hence may exist"? ciao, lele. -- nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia. lele@metapensiero.it | -- Fortunato Depero, 1929.

On Apr 12, 2013, at 03:53 PM, Lele Gaifax wrote:
Oops, yes. Good catch! I changed it to: Because ``Colors`` and ``OtherColors`` are unrelated enumerations, their values are not equal, and thus they may exist in the same set, or as distinct keys in the same dictionary:: -Barry

On Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> wrote:
Link to the PEP: http://www.python.org/dev/peps/pep-0435/ [it's also pasted fully below for convenience].
This looks great. There's just one bit I don't understand. I'm sure it was discussed in the python-ideas thread, but the discussion of it in the PEP does not provide any motivation for the decision.
[...]
This is the part that I don't understand. Enums *clearly* have an ordering, since the iteration order is defined and stable. Why should I not be allowed to compare values from the same Enum type? There are certainly use cases where that is very useful. To give you a concrete use case: consider response codes from a client server application constructed the way typical internet protocols are. We might have: class MyAppCode(Enum): ok = 200 command_complete = 201 status_response = 202 help_response = 203 service_ready = 204 signoff_accepted = 205 temporary_failure = 400 service_not_available = 401 server_error = 402 out_of_resources = 403 error = 500 syntax_error = 501 command_not_implemented = 502 access_denied = 503 resource_not_found = 504 It can be quite handy to be able to say things like: code = myapp.operation(opstring) if MyAppCode.temporary_failure < code < MyAppCode.error: myframework.requeue(opstring, code=code) return False elif code > MyAppCode.error: raise AppError(code) .... In talking to an existing internet protocol it would be natural to use IntEnum and this issue would not arise, but I have recently worked on an application that had *exactly* the above sort of enumeration used internally, when it would have been totally appropriate to use Enum rather than IntEnum. The ap has several places where an ordered comparison against the enum is used to check if a code is in the error range or not. --David

I think the reason they are not supporting __lt__, __gt__,etc. is because ints are optional values for enums, therefore it wouldnt be a good idea to compare enums of different types in that way. example:
and doing
MyEnum.fir >= MyEnum.thir would give unexpected results, therefore not making it a great idea
On Fri, Apr 12, 2013 at 9:58 AM, R. David Murray <rdmurray@bitdance.com>wrote:

On Fri, 12 Apr 2013 10:19:29 -0400, Davis Silverman <sinistersnare@gmail.com> wrote:
That's why I included the bit about iterating the values. The ordering *is* defined. I find it much more surprising for that ordering to be inaccessible via the comparison operators. I think either the iteration order should be undefined (like a set or dict), or the comparison operations should work. I'd prefer the latter, because of the use case I outlined. --David

i agree with that, and good point, i guess i misunderstood. enums could be a nice edition, i cant wait to see how this goes. --Sinistersnare On Fri, Apr 12, 2013 at 10:23 AM, R. David Murray <rdmurray@bitdance.com>wrote:

On 04/12/2013 07:51 AM, Barry Warsaw wrote:
Huh?? From the PEP ============ The ``Enum`` class supports iteration. Iteration is defined as the sorted order of the item values:: >>> class FiveColors(Enum): ... pink = 4 ... cyan = 5 ... green = 2 ... blue = 3 ... red = 1 >>> [v.name for v in FiveColors] ['red', 'green', 'blue', 'pink', 'cyan'] -- ~Ethan~

On Apr 12, 2013, at 09:58 AM, R. David Murray wrote:
Nick brought this up in private email, and my response was basically that iteration order for Enums should not be guaranteed, even if that happens to work in the current implementation. The reason why it works in the current implementation is purely to provide predictable reprs. Of course, we could sort the values for the repr and remove the sorted() call in EnumMetaclass.__iter__() but then we would have to add it back for IntEnums, since we *do* want to make explicit iteration order guarantees for IntEnums, as they also have full rich comparisons. I'm just not sure it's worth it from an implementation point of view. I will update the PEP to make this more clear.
Why Enums and not IntEnums? Enums will not have ordered comparisons, but IntEnums will. -Barry

On Fri, 12 Apr 2013 10:50:44 -0400, Barry Warsaw <barry@python.org> wrote:
OK.
To take advantage of their incommensurability with other Enums. It's not a big deal, though; I'm more concerned that the API be internally consistent. I presume that one could always define an Enum subclass and provide comparison methods if desired :) --David

1) Is there limitation that EnumValues themselves must be immutable? 2) I'm most excited by the prospect of using these Enums as function defaults. I've not worked it through fully, but I'm guessing the following will work?
class Colors(Enum): ... red = (256, 0, 0)
3) Enums are generally used for defining constants - Is there a case to be made for using capitals in the 435 as PEP8 suggests, or are enums a special case? (http://www.python.org/dev/peps/pep-0008/#constants) 4) Is there an easy way to create custom EnumValues subclasses? In particular it'd be nice to be able to change the __repr__ in some cases to hide the value itself, which is often not important. Sorry if this has already been discussed elsewhere (I've only recently signed up to the dev mailinglist). On 12 April 2013 16:23, R. David Murray <rdmurray@bitdance.com> wrote:

On Apr 12, 2013, at 04:57 PM, Phil Elson wrote:
1) Is there limitation that EnumValues themselves must be immutable?
I'm not sure it makes sense to have mutable EnumValues, but yes, they are immutable in the sense that you cannot change their underlying value (well, without hackery).
Sure, why not? :) >>> from flufl.enum import Enum >>> X = Enum('X', 'a b c') >>> def doit(default=X.b): ... pass ... >>> from inspect import signature >>> print(signature(doit)) (default=<EnumValue: X.b [value=2]>)
I dunno, I like that the PEP isn't shouting at me. :)
Yes, although we aren't documenting it so we don't get locked into the API. If you look at the flufl.enum implementation, you'll see this is how we implement IntEnums. -Barry

Is there a convenient way to change this behavior, namely to indicate that conflicts are acceptable in a given Enum? While I like the idea of catching mistaken collisions, I've seen far too many C/C++ scenarios where multiple names map to the same value. This does raise questions, as it's unclear whether each name should get its own representation, or whether it's better to let DupEnum.name1 is DupEnum.name2 be True. (For the latter behavior, would adding DupEnum.name2 = DupEnum.name1 after the class declaration work today?) Michael -- Michael Urman

On Apr 12, 2013, at 09:03 AM, Michael Urman wrote:
Currently there is not. This behavior is defined in the metaclass.
(For the latter behavior, would adding DupEnum.name2 = DupEnum.name1 after the class declaration work today?)
Yes, but the repr/str of the alias will show the original value. >>> from flufl.enum import Enum >>> Colors = Enum('Colors', 'red green blue') >>> Colors.black = Colors.blue >>> Colors.black <EnumValue: Colors.blue [value=3]> >>> Colors.black is Colors.blue True -Barry

On Fri, Apr 12, 2013 at 9:30 AM, Barry Warsaw <barry@python.org> wrote:
That satisfies my concern. This gives an author the means to provide two names for a single value, and a way to choose which one is canonical. It's easy to imagine some corner cases related to persisting those values and then retrieving them with a later enum definition that changes the canonical name, but if you store raw values or names it should be easy enough to work around such things. Michael

On 12.04.13 15:55, Eli Bendersky wrote:
This is unnecessary because enumerations are iterable. Colors.__members__ is equal to [v.name for v in Colors] and the latter looks more preferable, because it does not use the magic method.
Does the enumeration's repr() use str() or repr() for the enumeration values? And same question for the enumeration's str().
How to get the enumeration value by its value?
It's unexpected if values of the enumeration values have the natural order. And values of the enumeration values *should be* comparable ("Iteration is defined as the sorted order of the item values").
Enumeration values ------------------
There is some ambiguity in the term "enumeration values". On the one hand, it's the singleton instances of the enumeration class (Colors.red, Colors.gree, Colors.blue), and on the other hand it is their values (1, 2, 3).
But if the value *is* important, enumerations can have arbitrary values.
Should enumeration values be hashable? At least they should be comparable ("Iteration is defined as the sorted order of the item values").
What is ``isinstance(Shape.circle, int)``? Does PyLong_Check() return true for ``IntEnum`` values?
Enumerations created with the class syntax can also be pickled and unpickled::
This does not apply to marshalling, I suppose? Perhaps this is worth to mention explicitly. There may be some errors of incompatibility.
Why the enumeration starts from 1? It is not consistent with namedtuple, in which indices are zero-based, and I believe that in most practical cases the enumeration integer values are zero-based.
Use-cases in the standard library =================================
The Python standard library has many places where named integer constants used as bitmasks (i.e. os.O_CREAT | os.O_WRONLY | os.O_TRUNC, select.POLLIN | select.POLLPRI, re.IGNORECASE | re.ASCII). The proposed PEP is not applicable to these cases. Whether it is planned expansion of Enum or additional EnumSet class to aid in these cases?

On Sat, Apr 13, 2013 at 1:31 AM, Serhiy Storchaka <storchaka@gmail.com>wrote:
Right. Fixed (removed this part because it's redundant), thanks.
str
I've updated the PEP since then. It also shows how to use __getitem__ syntax to access by value.
I agree, but not sure how to resolve it. I hope it's clear enough from the context.
See long discussion previously in this thread.
Yes. IntEnumValue (the value class underlying IntEnum) subclasses int.
No special provision has been made for marshalling. The ``Enum`` class is callable, providing the following convenience API::
Why the enumeration starts from 1? It is not consistent with namedtuple, in
which indices are zero-based, and I believe that in most practical cases the enumeration integer values are zero-based.
I don't know if there was a special reason for this. Perhaps backwards compatibility with existing flufl.enum APIs. Barry may know more about this.
Use-cases in the standard library =================================
The Python standard library has many places where named integer constants
It is applicable, in the sense that os.O_CREAT etc can be IntEnum values. Their bitset operation results will be simple integers. It's not planned to add a special enum for this - this was ruled against during the Pycon discussions. Eli

OK, so I finally got tie to read the PEP. I like it, I really have missed Enums, this is awesome. That's all folks! //Lennart

On 13.04.13 15:43, Eli Bendersky wrote:
On Sat, Apr 13, 2013 at 1:31 AM, Serhiy Storchaka <storchaka@gmail.com>wrote:
On 12.04.13 15:55, Eli Bendersky wrote:
May be use "enumeration items" or "enumeration members" if instances of the enumeration class have in mind? And left "enumeration names" and "enumeration values" for sets of corresponding attributes (.name and .value) of instances.
I think this requirements (hashability and comparability (for repr() and iteration)) should be mentioned explicitly.
But IntEnum is useless in such cases because a resulting mask will be an integer an will lost its convenient printable representation. There is almost no benefit of IntEnum before int constant.

[Eli]
On Sat, Apr 13, 2013 at 7:30 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
If you really wanted that you could define an int subclass that does better and use that -- IntEnum is just one example of how you can override the value type of Enum. (And yes, I am now +1 on documenting this mechanism.) -- --Guido van Rossum (python.org/~guido)

On Apr 13, 2013, at 08:33 AM, Guido van Rossum wrote:
(And yes, I am now +1 on documenting this mechanism.)
Here's what I've added to the flufl.enum documentation: Customization protocol ====================== You can define your own enumeration value types by using the ``__value_factory__`` protocol. This is how the ``IntEnum`` type is defined. As an example, let's say you want to define a new type of enumeration where the values were subclasses of ``str``. First, define your enumeration value subclass. >>> from flufl.enum import EnumValue >>> class StrEnumValue(str, EnumValue): ... def __new__(cls, enum, value, attr): ... return super(StrEnumValue, cls).__new__(cls, value) And then define your enumeration class. You must set the class attribute ``__value_factory__`` to the class of the values you want to create. >>> class StrEnum(Enum): ... __value_factory__ = StrEnumValue Now, when you define your enumerations, the values will be ``str`` subclasses. :: >>> class Noises(StrEnum): ... dog = 'bark' ... cat = 'meow' ... cow = 'moo' >>> isinstance(Noises.cow, str) True -Barry

On Apr 13, 2013, at 11:31 AM, Serhiy Storchaka wrote:
__members__ was really a holdover from earlier versions. I've removed this, but I've added an __dir__().
No, enumeration values have different reprs and strs.
And same question for the enumeration's str().
Enumerations share a repr and str (well, technically, Enums don't define a __str__()).
Use getitem syntax: >>> from flufl.enum import Enum >>> A = Enum('A', 'a b c') >>> A[2] <EnumValue: A.b [value=2]>
This is one reason why Enums are not comparable except by equality. While I think it's not a good idea to mix the types of Enum item values, it *is* possible, and I don't think we should add strict checks to enforce this for the base Enum. Thus we cannot guarantee that <, >, <=, or >= will not throw a TypeError. IntEnums do define these because they can be guaranteed to succeed, since their enumeration item values are guaranteed to be integers.
I've just made sure that the flufl.enum using.rst document is consistent here. The terms I'm using are "enumeration item" to define things like Colors.red and "enumeration item value" (or sometimes just "enumeration value") to define the value of the enumeration item, e.g. 2, and it's available on the .value attribute of the item. "Enumeration item name" is essentially the attribute name, and is available on the .name attribute of the item.
Given my previous responses, these questions should be already answered.
True. Yes, because IntEnumValues inherit from int.
There are several reasons: * It's been that way since day 1 <wink> of the package * Zero is special in a way; it's the only false integer value * This is very easy to override * If you're using the auto-numbering convenience API, then you don't care about the values anyway
IntEnums work fine for these cases, but it's true that the result of a logical operation is an int and not a subclass with a nice repr. Contributions are welcome. -Barry

Thank you for your answers, Barry. Eli already answered me most of my questions. 20.04.13 22:18, Barry Warsaw написав(ла):
Yes, values can have different reprs and strs (but ints haven't). What of them uses repr of an enumeration item? I.e. what is str(Noises): '<Noises {dog: bark}>' or '<Noises {dog: "bark"}>'? class Noises(Enum) dog = 'bark' flufl.enum uses str(), but is it intentional? If yes, than it should be specified in the PEP.
Eli and you have missed my first question. Should enumeration values be hashable? If yes (flufl.enum requires hashability), then this should be specified in the PEP. If no, then how you implements __getitem__? You can use binary search (but values can be noncomparable) or linear search which is not efficient.

Hello, Le Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> a écrit :
I'm having a problem with the proposed implementation. I haven't found any mention of it, so apologies if this has already been discussed:

Hello, (sorry for the previous message attempt - my mouse pointer hit the send button before I was finished with it) Le Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> a écrit :
I'm having a problem with the proposed implementation. I haven't found any mention of it, so apologies if this has already been discussed:
It would really be better if instances were actual instances of the class, IMO. Regards Antoine.

On Tue, Apr 23, 2013 at 11:44 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Looking at the source (https://bazaar.launchpad.net/~barry/flufl.enum/trunk/view/head:/flufl/enum/_...), I'm not seeing any fundamental technical issues with merging the Enum and EnumValue class definitions, and then using "cls" where the metaclass code currently uses "cls.__value_factory__" (even for the backwards compatible variant, the 2v3 metaclass issue isn't a problem, you can just define a _BaseEnum class with the right metaclass using the 2 & 3 compatible notation and inherit from that in a normal class definition) However, there's one non-technical aspect of such a merger which does concern me, which is the fact that you would lose the distinct docstrings for the class and the values:
EnumValue('Color', 'red', 12) prints as 'Color.red' and can be converted to the integer 12. So I'm not sure the PEP has made the wrong choice here, but I agree the point is at least worth mentioning. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Le Wed, 24 Apr 2013 00:22:40 +1000, Nick Coghlan <ncoghlan@gmail.com> a écrit :
You can work it around by making __doc__ a descriptor that behaves differently when called on a class or an instance. There is a slight metaclass complication because __doc__ is not writable on a class (but I suppose enums are already using a metaclass, so it's not much of an issue): class docwrapper: def __init__(self, class_doc, instance_doc_func): self.class_doc = class_doc self.instance_doc_func = instance_doc_func def __get__(self, instance, owner): if instance is None: return self.class_doc else: return self.instance_doc_func(instance) def instancedocwrapper(instance_doc_func): class metaclass(type): def __new__(meta, name, bases, dct): dct['__doc__'] = docwrapper(dct['__doc__'], instance_doc_func) return type.__new__(meta, name, bases, dct) return metaclass class D(metaclass=instancedocwrapper( lambda self: "My instance:{}".format(self.x))): """My beautiful, documented class.""" def __init__(self, x): self.x = x class E(D): """My magnificent subclass.""" print("class doc:", D.__doc__) print("subclass doc:", E.__doc__) print("instance doc:", E(5).__doc__) Note that the builtin help() function always displays the class's __doc__, even when called on an instance which has its own __doc__. Regards Antoine.

I gotta say, I'm with Antoine here. It's pretty natural (also coming from other languages) to assume that the class used to define the enums is also the type of the enum values. Certainly this is how it works in Java and C++, and I would say it's the same in Pascal and probably most other languages. This doesn't seem to matter much today, but various people are thinking about adding optional type annotations to Python code (use cases include hints for liters and better suggestions for IDEs). What would be more natural than being able to write class Color(Enum): RED = 1 WHITE = 2 BLUE = 3 ORANGE = 4 class Bikeshed: def paint(self, hue: Color, opacity: int): ... It would be awkward if the 'how' parameter had to be annotated with Color.ValueClass, or if the type checkers had to learn to imply .ValueClass if an attribute was labeled with an Enum type. Enums just aren't special enough. I find the docstring issue secondary. --Guido On Tue, Apr 23, 2013 at 7:57 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
-- --Guido van Rossum (python.org/~guido)

On Tue, 23 Apr 2013 08:11:06 -0700, Guido van Rossum <guido@python.org> wrote:
Well, I guess I can wrap my head around it :) An Enum is an odd duck anyway, which I suppose is one of the things that makes it worth adding. --David

On Tue, Apr 23, 2013 at 8:31 AM, R. David Murray <rdmurray@bitdance.com> wrote:
Sorry, you're being to literary/poetic. I can't tell what you meant by this response. -- --Guido van Rossum (python.org/~guido)

On Tue, 23 Apr 2013 08:44:21 -0700, Guido van Rossum <guido@python.org> wrote:
I was alluding to the fact that in Python we usually work with instances of classes (not always, I know, but still...), but with Enum we are really using the class as a first level object. Given that, breaking my (questionable?) intuition about isinstance should not be unexpected. And by that last phrase I meant to refer to the fact that getting this right is obviously non-trivial, which is one of the things that makes it worth adding as a Python feature. --David

On Apr 23, 2013, at 8:11 AM, Guido van Rossum wrote:
Furthermore, you could define methods/fields on enum values, like Java's enums. E.g.: Errno.EBADF.strerror() -> 'Bad file descriptor' Easily adapt to additional fields: class Protocol(Enum): HOPOPT = 0, "IPv6 Hop-by-Hop Option" ICMP = 1, "Internet Control Message" IGMP = 2, "Internet Group Management" @property def value(self): return self._value[0] @property def description(self): return self._value[1] -- Philip Jenvey

On Apr 23, 2013, at 01:20 PM, Philip Jenvey wrote:
Furthermore, you could define methods/fields on enum values, like Java's enums.
There's no reason why you couldn't do this now, even with the class separation. I've documented the customization protocol, so just add these methods to the class you set in __value_factory__. No need to conflate the classes, which I think just adds unnecessary complexity and confusion. -Barry

On Tue, 23 Apr 2013 15:44:58 +0200, Antoine Pitrou <solipsis@pitrou.net> wrote:
The first False looks correct to me, I would not expect an enum value to be an instance of the class that holds it as an attribute. The second certainly looks odd, but what does it even mean to have an instance of an Enum class? --David

Le Tue, 23 Apr 2013 10:24:18 -0400, "R. David Murray" <rdmurray@bitdance.com> a écrit :
Perhaps I should have added some context:
It is simply the same as a __getattr__ call. That said, I don't see why it wouldn't make sense for an enum value to be an instance of that class. It can be useful to write `isinstance(value, MyEnumClass)`. Also, any debug facility which has a preference for writing out class names would produce a better output than the generic "EnumValue". Regards Antoine.

On Tue, 23 Apr 2013 16:33:02 +0200, Antoine Pitrou <solipsis@pitrou.net> wrote:
Ah. I'd be looking for a bug every time I saw isinstance(value, myEnumClass). A better class name for values and an API for getting that class from the EnumClass would be nice, though. (So that you could write "isinstance(value, MyEnumClass.ValueClass)", say.) --David

On 4/23/2013 10:53 AM, R. David Murray wrote:
Reading what you have wrote, it seems that the issue is whether you consider an instance of Enum a "thing" or a "class of things". If you think of it as a "thing", then "C" is a object that has attributes for other "things" that are not like "C". However, if you think of "C" as a "class of things", then "C" having attributes that are instances of it's type is completely natural. Fundamentally, the question is whether an instance of Enum is a new type or an instance. And for me, it's a new type and I expect enum values to be instance of that type. -Scott -- Scott Dial scott@scottdial.com

On 4/23/2013 11:58 AM, Guido van Rossum wrote:
You seem to be mixing up classes and metaclasses.
I was trying to explain it in more plain terms, but maybe I made it even more confusing.. Anyways, I agree with you that isinstance(Color.RED, Color) should be True. -- Scott Dial scott@scottdial.com

On Apr 23, 2013, at 10:53 AM, R. David Murray wrote:
I think if we did this, the attribute should be the same name as the one used to customize the value factory. Okay, this is horrible, but you could use isinstance(value, MyEnumClass.__value_factory__) The only thing stopping you from doing this right now is that when __value_factory__ is not given, a default is used which is not available on that attribute. That's easily corrected though. 1) really, to be consistent with the documentation, this should be __item_factory__ since the attributes of the enum class are called "items", while items have an underlying value (e.g. A.b.value). 2) A better name than either __item_factory__ or __value_factory__ is welcome, though I guess that will spawn another bikeshedding, soul-destroying centi-thread. ;) 3) I'd like to make the implementation names consistent with the documentation in the next version of flufl.enum. -Barry

On Apr 23, 2013, at 04:33 PM, Antoine Pitrou wrote:
These semantics seem very weird to me, but at least we have a principled way to lie about it in Python 3. We could add this to the metaclass: def __instancecheck__(cls, instance): return instance.enum is cls or cls in instance.enum.__bases__ Thus: >>> X = Enum('X', 'a b c') >>> Y = Enum('Y', 'z y x') >>> class Z(Y): ... d = 4 ... e = 5 ... >>> isinstance(Z.d, Y) True >>> isinstance(Z.d, Z) True >>> isinstance(Z.d, X) False >>> isinstance(Y.z, Y) True >>> isinstance(Y.z, Z) False -Barry

R. David Murray wrote:
This attitude baffles me. In most other languages having a notion of an enum, when you define an enum, you're defining a type. The name of the enum is the name of the type, and its values are instances of that type. Why should our enums be any different? -- Greg

On Wed, 24 Apr 2013 11:37:16 +1200, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Obviously they don't need to be, since people have discussed how to implement this. But I am primarily a Python programmer, so my intuition is based on my Python experience, not on what other languages do. Given: MyEnum(Enum): a = 1 b = 2 I understood that 'a' and 'b' are class attributes, where the int value had been transformed into instances of a (separate) value class rather than being ints. The fact that there was a specific value class had been discussed. If 'a' is now an instance of MyEnum, then I would expect that: MyEnum.a.b would be valid (b being an attribute of the MyEnum class which should therefore be accessible from an instance of that class). That seems a bit odd, and based on my Python-only mindset, I saw no particular reason why an enum value *should* be instance of the enum class, since it would lead to that oddity. (Well, I'm not sure I was concious of that *particular* oddity, but in general it seemed like an odd thing to have class attributes of a class be instances of that class when the set of class attributes was the most important thing about that class...). It seemed more natural for the values to be instances of a separate value class. Now, given that I was viewing the Enum as being a collection of attributes whose values were instances of a different class, what would it mean to create an instance of the Enum class itself? You'd have an instance with access to those class attributes...but the getitem wouldn't work, because that's on the metaclass. You'd really want the Enum class to be a singleton...the important thing was that it was an instance of the metaclass, its instances would be pretty useless. I don't have any *problem* with enum values being instances of the class. If you make the attribute values instances of the enum class, then yes instances of enum class have a meaning. And then having attributes of the class be instances of the class makes perfect sense in hindsight. It's just not where *my* Python-only intuition, or my understanding of the discussion, led me. I feel like I'm revealing my ignorance or stupidity here, but what the heck, that's what was going through my brain and I might as well own up to it :). --David

On Tue, Apr 23, 2013 at 7:12 PM, R. David Murray <rdmurray@bitdance.com> wrote:
I'm not too worried. Clearly the Enum base class is magic, because, contrary to the behavior of regular class definitions, MyEnum.a is *not* just the integer 1 (even if it compares == to 1). At that point we may as well accept that the entire construct is magic, and the only consequences we can reason about are those that are documented as consequences of using this particular kind of magic. I'm not sure that we can implement a hack to prevent MyEnum.a.b from being an alias for MyEnum.b, but even if we cannot prevent that, I don't think we need to worry about it. (However, I suspect we can override __getattr__ to prevent this if it is considered too unsavory.) -- --Guido van Rossum (python.org/~guido)

R. David Murray wrote:
That is indeed a quirk, but it's not unprecedented. Exactly the same thing happens in Java. This compiles and runs: enum Foo { a, b } public class Main { public static void main(String[] args) { System.out.printf("%s\n", Foo.a.b); } } There probably isn't much use for that behaviour, but on the other hand, it's probably not worth going out of our way to prevent it. -- Greg

On Apr 23, 2013, at 10:24 AM, R. David Murray wrote:
Agreed, completely.
The second certainly looks odd, but what does it even mean to have an instance of an Enum class?
It only looks odd because it's using failed, duplicate, deprecated syntax. Does this look similarly odd?
isinstance(C[1], C) False
given that ``C[1] is C.a``? -Barry

On Thu, Apr 25, 2013 at 11:50 AM, Barry Warsaw <barry@python.org> wrote:
I don't know what's going on, but it feels like we had this same discussion a week ago, and I still disagree. Disregarding, the C[i] notation, I feel quite strongly that in the following example: class Color(Enum): red = 1 white = 2 blue = 3 orange = 4 the values Color.red etc. should be instances of Color. This is how things work in all other languages that I am aware of that let you define enums. -- --Guido van Rossum (python.org/~guido)

On Apr 25, 2013, at 02:54 PM, Guido van Rossum wrote:
I don't know what's going on,
Mostly that this is my first opportunity to chime in on the subject.
Is it enough that isinstance(Color.red, Color) returns True, or do you really-and-truly want them to be instances of Color? I still think it's weird, but I could accept the former if you're flexible on the latter, which in some sense is just an implementation detail anyway. -Barry

On Thu, Apr 25, 2013 at 3:02 PM, Barry Warsaw <barry@python.org> wrote:
Clearly this is a trick question. :-) I was told when this was brought up previously (a week ago?) that it would be simple to make it truly the same class. I suppose you were going to propose to use isinstance() overloading, but I honestly think that Color.red.__class__ should be the same object as Color. -- --Guido van Rossum (python.org/~guido)

On Apr 25, 2013, at 03:19 PM, Guido van Rossum wrote:
Clearly this is a trick question. :-)
A bit, yes. :)
I was told when this was brought up previously (a week ago?) that it would be simple to make it truly the same class.
It didn't sound simple to me, but I haven't seen any actual code yet.
Yes, a custom __instancecheck__() is two lines of code. I just can't get over the weirdness of a class having attributes which are actual instances of itself. -Barry

On Thu, Apr 25, 2013 at 3:29 PM, Barry Warsaw <barry@python.org> wrote:
TBH I had a hard time getting over the fact that even though the class said "a = 1", C.a is not the integer 1. But I did get over it. Hopefully you can get over *this* weirdness. -- --Guido van Rossum (python.org/~guido)

On 4/25/2013 3:37 PM, Guido van Rossum wrote:
:) The minute "metaclass" is involved, it is no longer a class, but a whatchamacallit. What blows people's minds regarding metaclasses is that they aren't necessarily what they look like. In this case, we are defining enumerations using a syntax that sort of looks like class attribute syntax, which are neither classes, nor types, but rather a collection of cooperative objects with shared methods in a shared subsidiary namespace. Perhaps instead of metaclass altering the behavior of classes, it might have been less confusing to simply define a new keyword "whatchamacallit" (or "metaclass") that have behaviors defined by the various methods defined in connection with it... while the existing techniques exist and would require a deprecation cycle to eliminate, it may not be too late to define alternate keywords that make it clearer that using such keywords, is making user-defined objects of some sort, class-like or -unlike, type-like or -unlike, object-like or -unlike, cooperative or not, etc., all by user definition. Any behavioral resemblance to classes would be only by user specification of same (perhaps such a specification of "same as class" should be easy). My question is, once an enumeration is defined, is there a way, short of element-by-element assignment, to import the individual enumeration instances into the current namespace, so that I can say "red" instead of "Color.red" ? I understand the benefits of avoiding name collisions when there are lots of enumerations, and lots of opportunities for name collections between, say, RGBColor and CYMKColor... but there are lots of uses for enumerations where the subsidiary namespace is just aggravating noise.

On 04/25/2013 04:26 PM, Glenn Linderman wrote:
You mean something like: --> class Color(Enum): ... RED = 1 ... GREEN = 2 ... BLUE = 3 --> Color.register() # puts Color in sys.modules --> from Color import * # doesn't work in a function, though :( --> BLUE Color.BLUE Yeah, that would be nice. ;) A bit dangerous, though -- what if another module does the same thing, but its Color is different? Better would be: --> Color.export(globals()) # put the enumerators in globals --> RED Color.RED -- ~Ethan~

On 26/04/13 11:53, Ethan Furman wrote:
It needs to put foo.Color in sys.modules, where foo is the name of the defining module. Then you do from foo.Color import * The drawback being that you need to write the name of the module into the import statement. It's disappointing that the import syntax doesn't have a way of saying "this module". -- Greg

On 4/25/2013 4:53 PM, Ethan Furman wrote:
Something like that, but that works in a function too :)
Globals? locals should be possible too. Or even something like: with Color: BLUE RED Although the extra indentation could also be annoying. One wouldn't want the module defining Color to automatically 'export' the colors: but rather a way to request an 'export' them into a particular scope. That way the proliferation of names into scopes is chosen by the programmer. import module_containing_color module_containing_color.Color.export_enumerations( globals ) or import module_containing_color module_containing_color.Color.export_enumerations( locals ) Or maybe locals is implicit, and in the file scope of a module, locals are globals anyway, so doing module_containing_color.Color.export_enumerations() would make the enumerations available to all definitions in the file, but inside a class or def doing the same thing would make the names direct members of the class or locals in the function.

On 04/25/2013 07:09 PM, Glenn Linderman wrote:
Not in Py3 it doesn't: Python 3.2.3 (default, Oct 19 2012, 19:53:16) [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. --> def test(): ... from sys import * ... print('huh') ... File "<stdin>", line 1 SyntaxError: import * only allowed at module level
At least in Cpython, updating locals() does not work in functions.
locals() can't be implicit, at least not without deep black magic of inspecting frames in the call stack -- which is hardly portable. -- ~Ethan~

On 4/25/2013 7:25 PM, Ethan Furman wrote:
Parse error. "Something like that, but something like that that works in a function too :)" is what I meant. I understand that the feature you demonstrated doesn't work in Py3... that's why we need "something like that" rather than "that" :)
So what I'm hearing is that enumerations need to be a language feature, rather than a module: Can't combine Enum and EnumItem Can't import into locals The compiler could do those things, though.

I wonder how many programmers will even notice that this characteristic exists. Exactly the same weirdness occurs in Java, but I had never thought about it until this discussion came up, and I wondered "Hmmm... seems like this ought to happen in Java too", tried it out, and found that it did. -- Greg

On Fri, Apr 26, 2013 at 8:29 AM, Barry Warsaw <barry@python.org> wrote:
I'm the one who said I didn't see any obvious barriers to the merger, but I've realised there is one, and it's similar to one namedtuple struggles with: how to handle method definitions. With the current flufl.enum design, if you want to give instances of a particular enum additional behaviour, you can use a custom item type and add the methods there. Simple and relatively obvious. With a merged design, it becomes *really* hard to give the instances custom behaviour, because the metaclass will somehow have to differentiate between namespace entries that are intended to be callables, and those which are intended to be instances of the enum. This is not an easy problem to solve. So, while I was initially in the "merge them" camp, I'm back to thinking the core architecture of flufl.enum is correct, but there may be some API adjustment to do, such as: 1. Lose the __getitem__ on the metaclass, and replace that with __call__ 2. Ensure that isinstance(MyEnum.item, MyEnum) returns True (even though it isn't really) 3. Inspired by namedtuple, move the current Enum constructor functionality to an Enum._make() API (implemented either as a class method in Enum or as an ordinary method on the metaclass) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Apr 25, 2013 at 7:13 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
It's really sad that this technical problem exists (and I realize what it is) -- because the obvious syntax (whose equivalent works in Java, BTW) *seems* so natural: class Color: red = 1 white = 2 blue = 3 orange = 4 def wave(self, n=1): for _ in range(n): print('Waving', self) a = Color.red a.wave() Color.orange.wave(3)
If the above syntax won't work, that isinstance() outcome isn't really important. :-( Can't we do some kind of callable check? There may be some weird decorators that won't work, but they aren't likely to be useful in this context.
-- --Guido van Rossum (python.org/~guido)

On Fri, Apr 26, 2013 at 12:38 PM, Guido van Rossum <guido@python.org> wrote:
Yeah, it may not be as tricky as I feared: adding "not callable(attr_val)" to the conditions for deciding whether or not to convert a class attribute to an instance of the enum would likely suffice to address the method problem. You couldn't create an enum of callables, but that would be a seriously weird thing to do anyway.... Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 4/25/2013 7:49 PM, Nick Coghlan wrote:
But aren't all classes callable? But you are referring to the initial values of the items, but still, why should those be restricted from being any general object? Not being _functions_ is probably OK, but I'm not sure how strong the distinction is between functions and classes, regarding being callable... So objects are only callable if the class contains a def __call__, but still, that seems quite restrictive.

On 4/25/2013 8:22 PM, Greg wrote:
Perhaps so, but an enumeration of objects whose class defines __call__ would not be so weird. Does your __methods__ cure this problem? It looked interesting to me, but I'm not familiar enough with metaclass to be sure of anything about it :)

On Thu, Apr 25, 2013 at 8:39 PM, Glenn Linderman <v+python@g.nevcal.com> wrote:
an enumeration of objects whose class defines __call__ would not be so weird.
Seriously? You'd complexificate the basic usage in order to cater for such an esoteric use case? The *only* use cases that matter at all for enum values are ints and strings, and even the latter could be considered a luxury when compared to other languages' enums. -- --Guido van Rossum (python.org/~guido)

On 4/25/2013 9:19 PM, Guido van Rossum wrote:
No, I'd look for a solution/implementation that doesn't divide objects into "plain" and "esoteric" cases. Py3 now treats everything as objects. So an enumeration should be able to deal with any object as a value.

On Fri, Apr 26, 2013 at 10:36 AM, Guido van Rossum <guido@python.org> wrote:
I feel that this thread has lost track of it long ago. Some time back in the Enum discussions (some 350 messages ago or so), there was a proposal to have this: class Color(Enum): RED, BLUE, GREEN By doing some crazy-cool shenanigans. Although the syntax is great, it was rejected on the basis of being too magic. The recent proposals of folding Enum and EnumValue into one, having class members be instances of the class they're members of while supporting a bunch of other Enum requirements also go off the rails in terms of complexity and magic. 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. Eli

On Fri, Apr 26, 2013 at 11:17 AM, Eli Bendersky <eliben@gmail.com> wrote:
Perhaps. The thread certainly has lost focus -- there are separate subthreads about the ordering of items when iterating over the enum class and about the unification of the Enum and EnumValue classes, and perhaps others.
FWIW, I didn't think the syntax was great. "Great" syntax would have been something like enum Color: RED, BLUE, GREEN but introducing a new keyword isn't justifiable.
Yet the goal of the proposal is conceptual simplification: one class instead of two. Having class members that are also class instances is a non-issue. The complexity of the proposal is an implementation issue: we'd like to be able to define methods for the enum values, and the simplest way (for the user) to define methods for the enum values would be to allow def statements, possibly decorated, in the class. But now the implementation has to draw a somewhat murky line between which definitions in the class should be interpreted as enum value definitions, and which should be interpreted as method definitions. If we had access to the syntax used for the definition, this would be simple: assignments define items, def statements define methods. But at run time we only see the final object resulting from the definition, which may not even be callable in the case of certain decorators. I am still optimistic that we can come up with a rule that works well enough in practice (and the Zen rule to which I was referring was, of course, "practicality beats purity").
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. -- --Guido van Rossum (python.org/~guido)

On Fri, Apr 26, 2013 at 2:41 PM, Guido van Rossum <guido@python.org> wrote:
I agree, and thanks for the thoughtful reply to my half-rant ;-) More comments below.
But users are not exposed to two classes. And for us, implementors, keeping things separate is IMHO simplification. There's a conceptual difference between a value of an enumeration and a collection of such values. This difference helps, I think, make IntEnum possible relatively easily - we can actually have an enumerated value that is-a integer and can be used instead of integers in legacy code. This requirement will heap additional complexity on the alternative where the enum and enum value are unified into the same class.
My reference to Java was w.r.t. the static-typing-minded requirement that isinstance(Color.red, Color) == True. I don't think that 99% of use-cases will care about the type of Color.red, and those that do for obscure reasons can accept an obscure class (EnumValue-something) as long as they have something distinct to dispatch upon. Eli

Eli Bendersky wrote:
Not if you think of an enum as a type and a type as defining a set of values. From that point of view, the enum itself is already a collection of values, and introducing another object is creating an artificial distinction. -- Greg

On 04/26/2013 06:37 PM, Greg Ewing wrote:
I agree (FWIW ;). It seems to me that the closest existing Python data type is bool. bool is a type and has exactly two members, which are static/singleton/only created once. Enum is a metatype which we use to create a type with a fixed number of members which are static/singleton/only created once. The salient differences: with Enum we name the type and the members with Enum the members are also attributes of the type As a concrete example, consider: class WeekDay(Enum): SUNDAY = 1 MONDAY = 2 TUESDAY = 3 WEDNESDAY = 4 THURSDAY = 5 FRIDAY = 6 SATURDAY = 7 If we follow bool's example, then like True and False are of type(bool), TUESDAY should be of type(WeekDay). -- ~Ethan~

Guido van Rossum wrote:
Another way we could tell the difference is if the def statement used a different protocol for making bindings than assignment. Suppose a def statement used in a class body called __defitem__, if it exists, instead of __setitem__. Then the metaclass would be able to do different things for defs and assignments. -- Greg

On 4/26/2013 6:22 PM, Greg Ewing wrote:
Well, some assignments could be for non-enumeration items, once you start allowing EnumItem in the list. Some method of grouping enumeration items, or grouping non-enumeration items would solve the problem. class Color( Enum ): Enum.__enumerationItems__( red=1, green=2, blue=3, ) # other methods and assignments

On 04/26/2013 07:29 PM, Glenn Linderman wrote:
Or, if we go with the metaclass magic of re-using the class/type name (and who doesn't love metaclass magic??): class Color(Enum): red = Color(1) green = Color(2) blue = Color 3) look_ma_not_an_enum = 4 -- ~Ethan~

On 27/04/13 12:51, Ethan Furman wrote:
and from a later email:
I'm sorry, but all these suggestions are getting the API completely backwards by making the common case harder than the rare case. We're creating an Enum, right? So the *common case* is to populate it with enum values. 99% of the time, enumerated values will be all that we want from an enum. So that's the case that needs to be simple, not the rare case where you have a non enum value in an enum class. The common case (enum values in an Enum class) should be easy, and the rare cases (ordinary class-like attributes) possible. Explicit is better than implicit: if you want something to *not* be processed by the Enum metaclass, you have to explicitly mark it as special. Dunders excepted, because they *are* special enough to break the rules. Since dunders are reserved for Python, I'm happy with a rule that says that dunders cannot be set as enum values (at least not via the metaclass). Otherwise, everything inside an Enum class is treated as an enum value unless explicitly flagged as not. Here's a dirty hack that demonstrates what I'm talking about. class EnumValue: # Mock EnumValue class. def __new__(cls, name, obj): print("making enum {!s} from {!r}".format(name, obj)) return obj class MetaEnum(type): def __new__(meta, name, bases, namespace): cls = super().__new__(meta, name, bases, {}) for name, value in namespace.items(): if meta.isspecial(value): value = value.original elif not meta.isdunder(name): value = EnumValue(name, value) setattr(cls, name, value) return cls @staticmethod def isdunder(name): return name.startswith('__') and name.endswith('__') @staticmethod def isspecial(obj): return isinstance(obj, skip) class skip: def __init__(self, obj): self.original = obj class Example(metaclass=MetaEnum): red = 1 blue = 2 green = lambda: 'good lord, even functions can be enums!' def __init__(self, count=3): self.count = count food = skip('spam') @skip def spam(self): return self.count * self.food -- Steven

On 04/27/2013 11:45 AM, Steven D'Aprano wrote:
I should have made a better example. In production code, doc strings can be priceless, so encouraging them seems like a good idea: class Planet(IntEnum): MERCURY = enum(1, doc='closest planet to the sun (?)') VENUS = enum(2, doc='the one with two names') EARTH = enum(3, doc='home sweet home') random_value = 42 Of course, I think it would be even better if the name were 'Planet' instead of 'enum' as that would emphasize the fact that we are actually creating items of the enumeration inside the enumeration. Kind of a shorthand for: class Planet(IntEnum): def __init__(...): ... def blahblah(...): ... Planet.MERCURY = Planet(...) Planet.VENUS = Planet(...) Planet.EARTH = Planet(...) which is the way I've done it for other classes in a similar situation.
While I agree that the common case should be simple, I also disagree that everything (especially functions) should easily be an enumerated value; the nice thing about being explicit as to which are the values (using 'enum' for example) is that it can also be used to capture functions in the rare case where that's what is desired. -- ~Ethan~

On 04/27/2013 12:47 PM, Ethan Furman wrote:
Just a quick followup: It seems to me that the *most* common case will be a simple name mapping, in which case one can do: Planet = Enum._make('Planet', 'MERCURY VENUS EARTH') and be done with it. -- ~Ethan~

On Apr 27, 2013, at 2:57 PM, Guido van Rossum wrote:
Call me crazy, but might I suggest: class Planet(Enum, values='MERCURY VENUS EARTH'): """Planets of the Solar System""" I've always wanted something similar for namedtuples, such as: class Point(NamedTuple, field_names='x y z'): """Planet location with Sun as etc""" Especially when the common idiom for specifying namedtuples w/ docstrings looks similar but leaves a lot to be desired w/ the required duplication of the name: class Point(namedtuple('Point', 'x y z')): """Planet location with Sun as etc""" (Raymond's even endorsed the former): http://bugs.python.org/msg111722 -- Philip Jenvey

On 04/27/2013 04:07 PM, Philip Jenvey wrote:
Okay, you're crazy! ;) I must be too, 'cause I really like that suggestion. Works easily, simple metaclass (or simple addition to current metaclass). Very nice. -- ~Ethan~

On 04/27/2013 04:17 PM, Ethan Furman wrote:
Having said that, what does it look like for a longer enum? class Planet( Enum, names=''' MERCURY VENUS EARTH MARS SATURN JUPITER URANUS PLUTO ''', ): '''Planets of the Solar System''' Not sure I like that. Ah well. -- ~Ethan~

On Sat, Apr 27, 2013 at 5:10 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
The problem with this and similar proposals is that it puts things inside string quotes that belong outside them. -- --Guido van Rossum (python.org/~guido)

Steven D'Aprano <steve@pearwood.info> writes:
However, without knowing that the MetaEnum metaclass will do some magic here, there's no way to know that there's anything special about red, blue and green. So I think there's actually a lot of implicit stuff happening here. In contrast, class Example(metaclass=MetaEnum): red = EnumValue(1) blue = EnumValue(2) green = EnumValue(lambda: 'good lord, even functions can be enums!') def __init__(self, count=3): self.count = count def spam(self): return self.count * self.food Makes it very clear that red, blue will not be attributes of type int, even if one has never heard of Enums or metaclasses before. I don't think this syntax is making the common case hard. By the same logic, you'd need to introduce C-style i++ postincrement because having just "i += x" makes the common case with x=1 "hard" as well. Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On Sat, Apr 27, 2013 at 7:41 AM, Guido van Rossum <guido@python.org> wrote:
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 "__value_factory__" 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@gmail.com | Brisbane, Australia

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@gmail.com> wrote:
-- --Guido van Rossum (python.org/~guido)

On 04/27/2013 09:01 AM, Guido van Rossum wrote:
While this will certainly work, it means you can't have class variables that happen to be the same type as the enum -- so no int in an IntEnum, for example. The solution I like best is the helper class (called, originally enough, enum), and only those items get transformed: class Planet(IntEnum): MERCURY = enum(1) VENUS = enum(2) EARTH = enum(3) rough_pi = 3 # not transformed -- ~Ethan~

On Sat, Apr 27, 2013 at 10:04 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
If this means that the most plain vanilla enum definition still has to use the enum(i) notation, I'm against it. Why do you want rough_pi to be a class variable anyway? The whole point of an enum is that it's *not* a kitchen sink class. An enum for the planets will need other support code that doesn't live in the enum class -- it shouldn't be considered a general scope for miscellanea. (TBH, I think that using classes to scope variables is mostly misguided anyway -- the standard mechanism for scoping is the module.) -- --Guido van Rossum (python.org/~guido)

On 04/27/2013 10:35 AM, Guido van Rossum wrote:
The two primary use cases I see are the (1) quick give me some names to values so I can fiddle and experiment, and the (2) production code with nice docs and the whole shebang. For (1) I would just use the _make (or whatever it's called) to give me something; for (2) using 'enum()' so that a docstring can also be added (even encouraged ;) seems like a Good Thing. And no, I have no idea what rough_pi is doing there, besides being an example on an int that doesn't get transformed. ;) -- ~Ethan~

Guido van Rossum <guido@python.org> writes:
I think this is actually a big advantage. It makes it obvious that something special is going on without having to know that IntEnum uses a special metaclass. Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On 04/27/2013 07:12 PM, Greg Ewing wrote:
Overriding __init__ is a PITA because __init__ is also called when you do Planet(3) # get EARTH and __init__ was expecting a gravitational constant and radius (or something like that). A couple ways around that: 1) have the metaclass store the args somewhere special (e.g. _args), have __init__ look like `def __init__(self, value=None)`, and have the body treat _args as if it were *args 2) have a `_init` that the metaclass calls with the args instead of __init__ -- ~Ethan~

Ethan Furman wrote:
I don't much like either of those. It would be much nicer if one could just write an ordinary __init__ method and have it work as expected. It's possible to make it work, I think. The __call__ method of the metaclass is going to have to do something special anyway, so that Planet(3) can look up and return an existing instance instead of making a new one. And if it doesn't make a new instance, it's not going to call the __init__ method. -- Greg

On 04/27/2013 08:59 PM, Greg Ewing wrote:
Agreed, on both counts.
So far I've had that logic in __new__ (which, of course, has no control on whether __init__ is called); I'll check out __call__ as soon as I can. Thanks for the tip! -- ~Ethan~

On 04/27/2013 08:59 PM, Greg Ewing wrote:
It works beautifully! It's not even complicated because the metaclass __new__ uses object.__new__ to create the instances, so EnumType.__call__ is /only/ called in cases like Planet(3), or Planet('EARTH'). Sweet! -- ~Ethan~

On 04/26/2013 02:41 PM, Guido van Rossum wrote:
The rule I liked best is "ignore callables, descriptors, and anything with leading & trailing double underscores". Personally I'd modify that to simply "anything with two leading underscores" so you can have private variables. It seems Pythonic to me in that classes already treat all those things special. And if you want enums of any of those things you can instantiate & insert them by hand after the class definition. Does that fail in an important way? //arry/

On Sat, Apr 27, 2013 at 9:41 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Great, sounds like a plan. The exception for callables may not even be needed -- the callables we care about (and some non-callables, like properties) are all descriptors. Or do we care about nested class definitions? (The reason I'm not keen on a general exemption for callables is that some 3rd party objects you wouldn't necessarily expect to be callable actually are.) I agree on a general exemption for __dunder__ names. The problem with exempting __private is that by the time the metaclass sees them, they've already been mangled to _classname__private. And I could just about imagine a use case for having a private value in an enum. -- --Guido van Rossum (python.org/~guido)

On 04/26/2013 11:17 AM, Eli Bendersky wrote:
Although explicit reasons were not mentioned (and perhaps not even consciously recognized -- *sigh* someday I hope to be that good), there are very good ones beyond "being too magic" -- way too easy to introduce bugs: name lookup success looks exactly like name lookup failure, but the consequences were drastically different: class Color(Enum): BLACK RED GREEN BLUE class MoreColor(Enum): BLACK CYAN MAGENTA YELLOW BLACK in MoreColor is a bug that cannot easily be detected as it is a successful name lookup; the consequence is that CYAN, MAGENTA, and YELLOW are now off by one. Not being Dutch, I had to smack into that one before I gave up on the idea. -- ~Ethan~

On 26/04/13 13:22, Greg wrote:
I don't think iscallable will work, since that descriptors like staticmethod and classmethod aren't callable. Nor are properties. I think a solution may be an explicit decorator that tells the metaclass not to skip the object into an enum value: class Insect(enum.Enum): ant = 1 bee = 2 @enum.skip @classmethod def spam(cls, args): pass assert isinstance(Insect.spam, classmethod) One side effect of this is that now you can (ab)use the decorator to have regular data attributes. Whether that counts as a good thing or a bad thing, I leave up to others to decide... -- Steven

On 04/25/2013 10:01 PM, Steven D'Aprano wrote:
Another option is to check if the item is a descriptor (class, static, property, or other); yet another option is to check if the item is the type of the enum class (int for IntEnum, str for StrEnum, etc.). The code I posted earlier checks for callable and type -- checking for descriptor also would not be much more effort. -- ~Ethan~

On Fri, Apr 26, 2013 at 3:01 PM, Steven D'Aprano <steve@pearwood.info> wrote:
My point was there *are* broad categories that we can reasonably say "you can't use these as values in an enumeration". Callables are one, descriptors are probably another. Alternatively, we can flip it around and require that each enum definition nominate an expected value type (defaulting to int) and only convert class attributes that are instances of that type to instances of the enum class. Either can be made to work, so it's really Guido's call as to which he would prefer. As Barry noted, the next step is actually for someone to create a proof of concept that shows the merge is possible in practice, not just theory. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Steven D'Aprano wrote:
I don't think iscallable will work, since that descriptors like staticmethod and classmethod aren't callable. Nor are properties.
Hmmm, maybe we should look for a __get__ method as well? Enums of descriptors would seem to fall into the seriously-weird category as well. Or if, as Guido says, the only sensible things to use as enum values are ints and strings, just leave anything alone that isn't one of those. -- Greg

On 04/26/2013 12:34 AM, Greg Ewing wrote:
The standard Java documentation on enums: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html has an example enum of a "Planet", a small record type containing mass and radius--each of which are floats. I don't know whether or not it constitutes good programming, but I'd be crestfallen if Java enums were more expressive than Python enums ;-) FWIW I'm +0.5 on "the enum metaclass ignores callables and descriptors". This seems reasonably Pythonic, much more so than "ignore everything except ints and strings". And as long as we're special-casing it I think we should opt for flexibility. Certainly I see nothing wrong with enums of float, complex, Decimal, and Fraction, so I don't see a good place to draw the line with a whitelist. //arry/

On 04/26/2013 08:50 AM, Larry Hastings wrote:
A whitelist, if we go that route, should be on a per Enum basis -- so something like: class Planets(str, Enum): PLUTO = 'it is too a planet!' MERCURY = 'definitely a planet' SATURN = 'darn big planet' solar_mass = 82738273 # or some really big number light_year = 1827499 # another really big number (of feet!) def horrorscope(self, message): return ('%s will be in retrograde... ' 'don't communicate today.' % self) and only PLUTO, MERCURY, and SATURN would be converted to enums. -- ~Ethan~

26.04.13 18:50, Larry Hastings написав(ла):
This example requires more than features discussed here. It requires an enum constructor. class Planet(Enum): MERCURY = Planet(3.303e+23, 2.4397e6) VENUS = Planet(4.869e+24, 6.0518e6) EARTH = Planet(5.976e+24, 6.37814e6) MARS = Planet(6.421e+23, 3.3972e6) JUPITER = Planet(1.9e+27, 7.1492e7) SATURN = Planet(5.688e+26, 6.0268e7) URANUS = Planet(8.686e+25, 2.5559e7) NEPTUNE = Planet(1.024e+26, 2.4746e7) def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters @property def surfaceGravity(self): # universal gravitational constant (m3 kg-1 s-2) G = 6.67300E-11 return G * self.mass / (self.radius * self.radius) def surfaceWeight(self, otherMass): return otherMass * self.surfaceGravity This can't work because the name Planet in the class definition is not defined.

On 04/26/2013 09:27 AM, Serhiy Storchaka wrote:
The metaclass can define it easily: - have generic helper class (I call mine `attrs`) - __prepare__ creates an instance of the custom dict - __prepare__ then inserts the helper class into the custom dict with the same name as the (to be created) custom Enum type - return the custom dict to Python class is processed using custom dict - __new__ gets the custom dict back from Python - __new__ replaces all instances of the helper class with actual Enum instances (which it creates on the spot) - any other housekeeping necessary - __new__ returns the new Enum type, complete with all Enum instances Here's an example run: 8<----------planets.py------------------------------------------------ from aenum import Enum class Planet(Enum): MERCURY = Planet(3.303e+23, 2.4397e6) VENUS = Planet(4.869e+24, 6.0518e6) EARTH = Planet(5.976e+24, 6.37814e6) MARS = Planet(6.421e+23, 3.3972e6) JUPITER = Planet(1.9e+27, 7.1492e7) SATURN = Planet(5.688e+26, 6.0268e7) URANUS = Planet(8.686e+25, 2.5559e7) NEPTUNE = Planet(1.024e+26, 2.4746e7) def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters @property def surfaceGravity(self): # universal gravitational constant (m3 kg-1 s-2) G = 6.67300E-11 return G * self.mass / (self.radius * self.radius) def surfaceWeight(self, otherMass): return otherMass * self.surfaceGravity print(int(Planet.VENUS)) print(repr(Planet.VENUS)) print(Planet.VENUS.surfaceGravity) 8<----------planets.py------------------------------------------------ 8<----------actual run------------------------------------------------ 2 Planet('VENUS', 4.869e+24, 6051800.0, integer=2) 8.871391908774457 8<----------actual run------------------------------------------------ -- ~Ethan~

On 04/26/2013 09:27 AM, Serhiy Storchaka wrote:
It can't work because you inserted the word "Planet" there. If you omit the word "Planet", this would work fine with something like the metaclass instantiate-all-data-members behavior in flufl.enum 4. Here is the hack, demonstrated in Python 3: class Metaclass(type): def __new__(cls, name, bases, namespace): result = type.__new__(cls, name, bases, dict(namespace)) for name, value in namespace.items(): if not (callable(value) or name.startswith("__")): value = result(name, value) setattr(result, name, value) return result class Planet(metaclass=Metaclass): MERCURY = (3.303e+23, 2.4397e6) def __init__(self, name, value): self.mass, self.radius = value def surfaceGravity(self): return 6.67300E-11 * self.mass / (self.radius ** 2) def surfaceWeight(self, otherMass): return otherMass * self.surfaceGravity() print("If you weigh 175 pounds, on Mercury you'd weigh", Planet.MERCURY.surfaceWeight(175)) //arry/

Steven D'Aprano <steve@pearwood.info> writes:
In this case, wouldn't it be nicer to "decorate" those attributes that are meant to be enum values? I think having to use the class keyword to define something that really doesn't behave like an ordinary class is pretty confusing, and the following syntax would be a lot easier to understand at first sight: class Insect(enum.Enum): ant = enum.EnumValue(1) bee = enum.EnumValue(2) @classmethod def spam(cls, args): pass def ham(self, args): pass Obviously, EnumValue() would automatically assign a suitable number. Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On 26/04/2013 2:38 p.m., Guido van Rossum wrote:
Another possible solution: class Color: red = 1 white = 2 blue = 3 orange = 4 class __methods__: def wave(self, n=1): for _ in range(n): print('Waving', self) and have the metaclass pull the functions out of the __methods__ sub-object. -- Greg

On 04/25/2013 07:13 PM, Nick Coghlan wrote:
I'm probably going to regret asking this, but what's difficult with the following? 8<----------------------------------------------------------------------------------------- class EnumDict(dict): """ automatically assigns the next _int for an enum """ def __init__(yo): super().__init__() yo._allow_duplicates = False yo._value = 1 yo._base = 1 yo._enums = [] yo._type = None # object means enum, anything else means all must be of that type def __setitem__(yo, key, new_value): """ main purpose is to support auto-numbering of members """ existing = yo.get(key) if type(existing) is attrs: # attrs is a helper class raise TypeError('Attempted to reuse key: %s' % key) if not key[:2] == key[-2:] == '__': old_value = None if isinstance(new_value, attrs): old_value = new_value if new_value.integer is None: new_value = yo._value else: new_value = new_value.integer if not isinstance(new_value, int): raise TypeError( "an enum integer must be an instance of type <int>, not %s" % type(new_value) ) if not callable(new_value): # this if-else is probably not final if (isinstance(new_value, int) and old_value is not None or yo._type in (object, )): yo._check_duplicate_integer(new_value, key) yo._value = new_value yo._inc_integer() yo._enums.append(key) new_value = attrs(integer=new_value) elif yo._type is None: # random bunch of named constants yo._check_duplicate_integer(new_value, key) value = yo._value yo._inc_integer() yo._enums.append(key) new_value = attrs(value=new_value, integer=value) elif isinstance(new_value, yo._type): # single type of named constants if isinstance(yo._type, int): value = new_value else: value = yo._value yo._check_duplicate_integer(value, key) yo._inc_integer() yo._enums.append(key) new_value = attrs(value=new_value, integer=value) if old_value is not None: new_value, old_value.integer = old_value, new_value.integer dict.__setitem__(yo, key, new_value) def _inc_integer(yo): if yo._base == 1: yo._value += 1 else: if yo._value == 0: value = 1 else: value = floor(log(yo._value, 2)) value = 2 ** value value <<= 1 yo._value = value def _check_duplicate_integer(yo, new_value, key): for name, value in yo.items(): if not isinstance(value, attrs): continue if value.integer == new_value and name != key and not yo._allow_duplicates: raise TypeError('duplicate value for %s: %d' % (key, value)) 8<------------------------------------------------------------------------------------------- Basically, if the assigned value is not `attrs` or a literal of the same type as the enum, the metaclass ignores it. -- ~Ethan~

2013/4/26 Nick Coghlan <ncoghlan@gmail.com>
There is at least one more problem, enum inheritance, given: class Colors(Enum): red = 1 green = 2 blue = 3 class MoreColors(Color): cyan = 4 magenta = 5 yellow = 6 what type is MoreColors.red? -- 闇に隠れた黒い力 弱い心を操る

Piotr Duda wrote:
Given the implementation we're considering, it would probably be Colors. However, there's a worse problem with defining enum inheritance that way. The subtype relation for extensible enums works the opposite way to that of classes. To see this, imagine a function expecting something of type Colors. It knows what to do with red, green and blue, but not anything else. So you *can't* pass it something of type MoreColors, because not all values of type MoreColors are of type Colors. On the other hand, you *can* pass a value of type Colors to something expecting MoreColors, because every value of Colors is also in MoreColors. Moreover, suppose we have another type: class YetMoreColors(Colors): orange = 4 purple = 5 pink = 6 Now suppose a function expecting Colors gets an enum with the integer value 4. How should it be interpreted? Is it cyan or orange? What about if you write it to a database column and read it back? These considerations suggest to me that subclassing enums should be disallowed, or at least not officially supported. -- Greg

On 26/04/13 18:00, Greg Ewing wrote:
There are many places where Python demands an actual int, not a subclass. See the recent thread "Semantics of __int__, index". There's no reason why a function that expects a Color *must* accept subclasses as well. If it can, great. If it can't, document it and move on. It's not Color's responsibility to know everything about every subclass. Invert your thinking: the subclasses are in charge, not Color. Color can't be expected to give a value to 4. Only the subclass that defines it can. This is only a problem if you believe that subclassing == taxonomy hierarchy. It isn't. http://pyvideo.org/video/879/the-art-of-subclassing -- Steven

26.04.13 11:00, Greg Ewing написав(ла):
I propose do not use an inheritance for extending enums, but use an import. class Colors(Enum): red = 1 green = 2 blue = 3 class MoreColors(Enum): from Colors import * cyan = 4 magenta = 5 yellow = 6 An inheritance we can use to limit a type of values. class Colors(int, Enum): # only int values red = 1 green = 2 blue = 3 Colors.viridity = green

26.04.13 05:13, Nick Coghlan написав(ла):
What if use mixins? Shouldn't it work without magic? class ColorMethods: def wave(self, n=1): for _ in range(n): print('Waving', self) class Color(ColorMethods, Enum): red = 1 white = 2 blue = 3 orange = 4

On Apr 23, 2013, at 03:44 PM, Antoine Pitrou wrote:
Ignore the single argument call syntax for Enums please. As Eli pointed out, you have getitem syntax for this and the single argument call syntax is deprecated. It will be removed in a future version of flufl.enum and need not appear in stdlib enum. TOOWTDI. C.a and C[1] return the same object, and it seems perfectly natural to me that this object is *not* an instance of the enum class. In fact, it seems completely weird to me that C.a would be an instance of the enum class. It seems very rare that a class has attributes that are instances of that class. It's not even easy to do with traditional syntax. class Foo: a = Foo() b = Foo() c = Foo() Huh? -Barry

On Apr 25, 2013, at 01:18 PM, Ethan Furman wrote:
Not if you think of it as a lookup operation instead of an instantiation operation. It really is the former because neither syntax creates new enum item objects, it just returns an already existing one. -Barry

On Thu, Apr 25, 2013 at 2:17 PM, Barry Warsaw <barry@python.org> wrote:
I think it's important to stress what this syntax is actually going to be used for. No one (I hope) is actually going to write Animals(1) or Animals[1]. They will write Animals.ant - this is what enums are for in the first place! The way I see it, this syntax is for enabling *programmatic access* - if you pull the value from a DB and want to convert it to an actual enum value, etc. So do we really need to have two syntaxes for this? The call syntax already has other uses, and it's weird because: Enum(....) -> Creates new enums Animals(....) --> accesses values ?! This is contradictory Animals[...] to serve as a by-value lookup makes sense, though. Eli

On 04/25/2013 02:25 PM, Eli Bendersky wrote:
How about consistency? If I'm converting several types of items from a database I'd like to do something like: result = [] for field in row: type = get_type(field) # returns int, bool, str, or an Enum type result.append(type(field)) What you're suggesting means complicating the logic: result = [] for field in row: type = get_type(field) # returns int, bool, str, or an Enum type if isinstance(type, Enum): result.append(type[field]) else: result.append(type(field)) We just got NoneType fixed to actually return None instead of raising an error for this same type of scenario, why should we muddy it up again? -- ~Ethan~

On Thu, 25 Apr 2013 14:37:29 -0700, Ethan Furman <ethan@stoneleaf.us> wrote:
I haven't cared much about this particular bikeshed, but I find this a somewhat compelling argument. I'm working on a system that depends on exactly this standard behavior of (most?) built in types in Python: if you pass an instance or something that can be converted to an instance to the type constructor, you get back an instance. If Enums break that paradigm(*), someone would have to write a custom class that provided that behavior in order to use Enums with my system. I wouldn't say that was a show stopper, especially since my system may never go anywhere :), but it certainly is an exemplar of the issue Eli is talking about. --David (*) Hmm. NoneType(None) is still an error.

On 04/25/2013 03:46 PM, R. David Murray wrote:
Um, did you mean "of the issue Ethan is talking about"? 'Cause Eli is against it.
(*) Hmm. NoneType(None) is still an error.
Hmm, so it is. When I pushed for the change just having NoneType() work was sufficient. -- ~Ethan~

On Apr 25, 2013, at 02:25 PM, Eli Bendersky wrote:
Excellent point, and no, we don't :).
I think so too. :) Note that I discovered that the same two-value call syntax on Enum can be used on the derived classes. It's exactly the same as using subclassing syntax to extend an existing enum. E.g. >>> class A(Enum): ... a = 1 ... b = 2 ... c = 3 ... >>> class B(A): ... d = 4 ... e = 5 ... >>> B.a is A.a True >>> X = Enum('X', 'a b c') >>> Y = X('Y', (('d', 4), ('e', 5))) >>> Y.a is X.a True That's a nice symmetry. -Barry

On 04/25/2013 02:17 PM, Barry Warsaw wrote:
True, but I don't. ;) I think the closest comparable thing in Python is the boolean class; while True and False are not attributes of bool, they are the only two instances, and invoking bool is going to return one of the existing bool instances (which is to say, True or False). It's basically a conversion from whatever to bool. --> bool('something') # returns True --> bool(None) # returns False Similarly, an Enum converts a string or a number to it's comparable enumerator (right word, Stephen?) --> class Animal(Enum): ... ant = 1 ... bee = 2 ... fly = 3 --> Animal(2) # should return Animal.bee --> Animal('ant') # should return Animal.ant It seems to me that storing the instances on the class as attributes is mostly for convenience; we could just as easily not, and change the repr of enumerators to '<Animal> ant [int=1]'. -- ~Ethan~

Le Fri, 12 Apr 2013 05:55:00 -0700, Eli Bendersky <eliben@gmail.com> a écrit :
The PEP should mention how to get an enum from its raw value: >>> Colors[1] <EnumValue: Colors.red [value=1]> or: >>> Colors(1) <EnumValue: Colors.red [value=1]> It would perhaps be nice to have a .get() method that return None if the raw value is unknown: >>> Colors(42) ... ValueError: 42 >>> Colors.get(42) >>> Regards Anroine.

On Thu, Apr 25, 2013 at 2:42 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Yes, this is mentioned in the beginning of the same paragraph.
This syntax was considered initially but then rejected because it's confusing, and there already exists a way to lookup by value (Colors[1]). Eli

On Thu, Apr 25, 2013 at 5:29 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
It produces a deprecation warning in flufl.enum because flufl.enum specifically supported this earlier. It should not be supported in the stdlib implementation. The __call__ syntax has been repurposed for the convenience API:
The aforementioned deprecated syntax refers to __call__ with a single arguments (the convenience API by definition requires more than one). Eli

On Thu, Apr 25, 2013 at 8:46 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Because we already have a way to do that: Animals[1]. Why do you need two slightly different ways to do the same? Moreover, why do you want to make Animals.__call__ behave very differently based only on the number of args? This seems to be un-pythonic in multiple ways. Eli

On 04/25/2013 09:34 AM, Eli Bendersky wrote:
I think we're talking past each other (or I'm not awake yet ;). Animals is a class. Giving Animals a parameter (such as 1 or 'ant') should return the instance that matches. This is how classes work. I don't understand your assertion that there is another way to call Animals... do you mean something like: --> MoreAnimals = Animals('MoreAnimals', 'bird worm insect') ? -- ~Ethan~

On Thu, Apr 25, 2013 at 9:39 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Yes, this works in the current implementation. But I'm realizing that the recent proposals of making isinstance(Color.red, Color) True will turn things around anyway so this discussion may be moot. Eli

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 04/25/2013 12:39 PM, Ethan Furman wrote:
Animals is a class. Giving Animals a parameter (such as 1 or 'ant') should return the instance that matches.
Animals is *not* a class -- it just uses the class syntax as a convenient way to set up the names used to construct the new type. (This subtlety is why the metaclass hook is reputed to make peoples' brains explode).
This is how classes work.
Not really. Normal classes, when called, give you a new instance: they don't look up existing instances. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with undefined - http://www.enigmail.net/ iEYEARECAAYFAlF5fa4ACgkQ+gerLs4ltQ7FSwCgzhcoXonMO/7W+xYMpM4EvtTj nPIAnAkHtWxFMaU3dqfFUclNQkUcJ2FZ =C0/7 -----END PGP SIGNATURE-----

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On Apr 25, 2013, at 03:02 PM, Tres Seaver wrote:
Thanks for the great explanation! :) - -Barry -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAEBCAAGBQJReYSxAAoJEBJutWOnSwa/1a8QALXlY5GkrDTgjTNc+i3VFbpW nQ+5iYSUKk7E7A5HTE/GO1thG/3lVVLQpDvuTP+ETqHiVgZ/oKI0WFpfC7C6h85W U7qclwjfZ0kng529FscwNcICgPilHng/gOR4msatje59R9Sw54mDkIYDHllhYzdZ R1NeWzfBmKL9aZJ3xGaIKHEB6Cwwk6ZL0F7S87Voe9ipd6ihRPbJK4y574XPe/4i m8jIrEVHI/9KCiDEdJrMXX7u+61jLEmWW9UnMuDJkNItxg/oiVwknSd2munDh1Ti yf7dZPkfx4Dcarv3DH5K/D/wo8OaOiqnvrM0qTImQObCWk8RswnGT3/SJmBNsfmN gndc+UTcVDYsVHtr57SEc79Y6vEAgdOlkrOChgzEoXUh9DS2slYFGuVMajncRJTI RY14k2KzPy1FdPysADk3KW6BIZRvZHMvszNFcEQiMwrDv98zFPfCqsm1KME7eBSg 4fjD0e5f8V+yLCyeUMUZxB6KhFyPdDi53w9X2mOQx9TPJqOJPNS7kzgGcMxagLMI BEw+3L3c5B5FZSd9JIQeIm3r5Cfee9Dvfgcfd5Y3QsHzaGCSSermRXs0cqRS51Bl 4LT39RDm6E+rSguR8PSOabmhwUrfhr2KMzkfZXtR8RwStPH4Tii8zpiSgY88k9JD XUZvZxjFCwWl3syPcMNR =Eglp -----END PGP SIGNATURE-----

FWIW, If we're going ahead with the plan to make Colors.red an instance of Colors, maybe the class names used in examples should be singular, i.e. isinstance(Color.red, Color)? -- --Guido van Rossum (python.org/~guido)

On Apr 25, 2013, at 11:42 AM, Antoine Pitrou wrote:
No, this is a failed experiment. It's deprecated in flufl.enum and shouldn't make it into stdlib enum. getitem syntax is TOOWTDI.
class Methods(Enum): set = 1 get = 2 delete = 3 What is "Methods.get"? Catching the ValueError ``Methods[5]`` would raise doesn't seem so bad. -Barry
participants (29)
-
Antoine Pitrou
-
Barry Warsaw
-
Davis Silverman
-
Dirkjan Ochtman
-
Eli Bendersky
-
Ethan Furman
-
Glenn Linderman
-
Greg
-
Greg Ewing
-
Guido van Rossum
-
Larry Hastings
-
Lele Gaifax
-
Lennart Regebro
-
Michael Urman
-
MRAB
-
Nick Coghlan
-
Nikolaus Rath
-
Phil Elson
-
Philip Jenvey
-
Piotr Duda
-
R. David Murray
-
Russell E. Owen
-
Scott Dial
-
Serhiy Storchaka
-
Steven D'Aprano
-
Terry Jan Reedy
-
Tim Delaney
-
Tres Seaver
-
Victor Stinner