12 ms·
PEP 435 Accepted – Adding an Enum type to the Python standard library
- randlet 13y agoGuido's approval message: http://mail.python.org/pipermail/python-dev/2013-May/126112.html http://mail.python.org/pipermail/python-dev/2013-May/126112....
- gbin 13y agoI prefer the Michael Ford proposed syntax and would gladly port the implementation if possible. Having to specify either an ordinal number like in BASIC of 1980 or have to specify symbols as strings looks dodgy to me.
- Tobu 13y agoSince Enums are mostly unmagical (apart from the member wrapping), you can use a compact syntax: class Colors(Enum): red, green, blue = range(3)
- cpeterso 13y agoMichael Ford's proposed syntax looks much nicer. I don't understand why the enum values have to be assigned when they are first looked up. Why couldn't the commas in the definition signal that the enum members should have automatic values assigned at the definition?
- morphyn 13y agoDoesn't the 'functional API' make up for it ? `Animal = Enum('Animal', ['ant', 'bee', 'cat', 'dog'])`
- bsimpson 13y agoThe functional API could stand to be improved a bit: >>> Animal = Enum(['ant, 'bee', 'cat', 'dog']) >>> Animal.ANT 'ant' I'm not convinced it's useful to pass the class name into the functional API, but I would see value in having an easy API to create enumerated strings. It's easier to see what's going on in other environments (e.g. in a datastore, or in JavaScript) if you pass around string identifiers instead of ints. Here's the class I've been using to do that: class Constants(object): def __init__(self, names, enum = False): self._names = frozenset(names) if enum: for (i, name) in enumerate(self._names): setattr(self, name.upper(), i) else: for name in self._names: setattr(self, name.upper(), name.lower()) self.frozen = True def keys(self): return [name.upper() for name in self._names] def values(self): return [getattr(self, name.upper()) for name in self._names] def __setattr__(self, key, value): if getattr(self, 'frozen', False): raise Exception("Constants cannot be modified after instantiation") else: object.__setattr__(self, key, value) Example: >>> ImportMethod = Constants( >>> [ >>> 'email', >>> 'manual_upload', >>> 'api', >>> ] >>> ) >>> >>> print(ImportMethod.API) 'api'
- dietrichepp 13y agoThere is a technical reason why the class name is passed into the API, and it's the same for other functions which create types (collections.namedtuple(), and type(), for example). You want to be able to print out the class name when you print an enum, but there is no other reliable way to figure out the class name. In Python, a class is just another value, like functions and integers and its name is nothing special. When you define a class normally, the name and dictionary get passed to the type() function.
- BoppreH 13y agoThe PEP mentions these explicit values allow the creation of aliases in a very natural way. class Keys(Enum): space = 1 esc = 2 enter = 3 return = 3 # Alias to "enter". Also, less magic.
- IgorPartola 13y agoreturn = 3 shouldn't work.
- mbreese 13y agoyou know what they meant...
- morpher 13y agoInteresting that this is PEP 435 and the prior attempt was PEP 354.
- aleyan 13y agoThe permutations will continue until the PEP improves.
- eliben 13y agoYes, I noticed that curiosity :-) Not intentional! Luckily there's no need for more permutations - this PEP was accepted. I expect PEPs 534 and 543 to surface at some point, but it isn't likely they will deal with enums.
- deleted 13y ago[deleted]
- adrianmsmith 13y agoI don't think that enums should have values (i.e. numbers). Nearly every language does it that way but isn't that just because C originally implemented enums with numbers? Modern languages are much higher level. Java enums don't have any numeric equivalent value, they are just objects, with textual names if you must convert them into something more primative.
- Nursie 13y agoEfficiency/speed? The user doesn't necessarily need to have access to the raw numbers I guess, but when it comes down to comparisons/branching etc, numbers are pretty much always going to be fastest and simplest. And if they are going to be based on numbers underneath, why not let the user/coder/whatever have access to them?
- comex 13y agoAs an option, it can be handy in some cases (less in Python than C, but still somewhat); requiring that you write them out for all enums, however, is somewhat annoying. C itself gets this right: you can write '= 1' to choose a value or leave it out to assign one automatically, and in either case you can convert to an integer (actually, they're integers in the first place, but that's C).
- mikeash 13y agoEverything is numbers underneath, so you could ask the same question about all data types. For example, if pointers are going to be based on numbers underneath, why not let the programmer have access to them? Lots of good reasons, actually, which is why most languages don't provide that access. Many of those reasons apply to enumerations too.
- Nursie 13y agoWell, you've outed me as a C programmer there I guess :) I don't really subscribe to the idea that languages should force people onto rails. I love higher level languages for their rapid development of course, and the high level constructs. But there's something about being able to get your hands dirty, right in the guts of a problem...
- comex 13y agoThe syntax strikes me as somewhat strange. It requires writing out enum values (and thus avoiding duplicates or, for a cleaner look, just keeping them sorted), necessitating unnecessary effort when new values are added in the middle, and misleadingly implies that the enum values are merely class-level integer constants. I would have preferred a more explicit API that used a list of strings for the enumerators.
- maxerickson 13y agoSimilar to this? http://www.python.org/dev/peps/pep-0435/#functional-api http://www.python.org/dev/peps/pep-0435/#functional-api Having to throw in a " ".join(...) isn't that bad.
- ulope 13y agoNo need for join(). > It can be a whitespace-separated string of names, a sequence of names, a sequence of 2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to values.
- maxerickson 13y agoEven better. The many levels of skimming on display.
- oellegaard 13y agoI completely agree, this disgusts me: >>> Animal = Enum('Animal', 'ant bee cat dog') >>> Animal.ant <Animal.ant: 1> That being said, I welcome enums - I made classes to basically represent an enum a bunch of times.
- Toshio 13y agoInteresting. It looks like if you want to use enum in 2.7 you have to use a package called flufl[1]. [1] http://bazaar.launchpad.net/~barry/flufl.enum/trunk/view/head:/flufl/enum/_enum.py#L170 http://bazaar.launchpad.net/~barry/flufl.enum/trunk/view/hea...
- eliben 13y agoflufl.enum was the initial candidate for PEP 435, but the current approach changed and uses implementation techniques not available in Python 2.7 We may create a almost-compliant back-port for 2.7 as an external library though
- tshepang 13y agoYou sure it's not better spending the energy elsewhere? Python 3 needs more exclusive features to encourage people to migrate from Python 2.
- chrismorgan 13y ago"Almost compliant" would mean compliant except for guaranteeing the correct order of iteration, would it not?
- yen223 13y agoWhat purpose would Enums serve in a dynamically-typed language?
- oellegaard 13y agoEven though dynamically typed, it still have the concept of types. There are good examples on how it works in the PEP.
- jerf 13y agoAnd more the the point, Python is strongly typed in the sense that it has very few automatic conversions between types, in contrast to, say, Perl. As the PEP says, this means that you can't accidentally compare an enum value of one type to an enum value of another without a runtime error occurring, instead of an infuriatingly subtle bug. Python may defer its type checking to run time, but it still has it.
- eliben 13y agoRead the PEP motivation :-) It's still useful to have mnemonic names for special values, with nice string representations and some guarantees on what is equal to what.
- herge 13y agoCould you not use mock.sentinel values as an enum? Also, you can create a poor man's enum from 2.7+ with: from collections import namedtuple myenum = namedtuple('myenum', ['a', 'b', 'c'])(range(3)) myenum.a == myenum.a
- viscanti 13y agoThat's not even a poor man's enum, it's just an enum. Named tuples can be used for everything an enum can be. This doesn't seem like a necessary PEP, but I've made my peace a long time ago about Python's bloated standard library. It's not the end of the world, and I still love Python, but this doesn't seem necessary to me.
- iso8859-1 13y agoYou don't have methods and you don't have a proper repr(). Also, even if you don't assign numbers, the fields are still ordered (which they shouldn't be).
- e1ven 13y agoCan someone help me understand how this helps? I'm only a mediocre programmer, and I'm having trouble understanding the implications. It seems like you declare these like any other class. So what's the difference between this, and creating a class full of ints? If I have class Color: red = 1 blue = 2 etc, wouldn't I get the same thing? Is the difference that you don't need to instantiate an instance of it? I know I'm being dense, and I've read the PEP, but it's not quite clicking yet.
- famousactress 13y agoIn this case, the attributes are not ints.. so If you defined that class functions that took it's attributes would need to accept an int, and if I passed max-int or something it'd be up to your function to go figure out whether my argument was a valid value for Color. The enum class gives you that stuff. The argument you'd expect would be instanceof(arg, Color) == True (I think that's how they did it) and if it was, you'd be assured that the value was a valid value for Color. So you could do it yourself, sure.. but to get all the little benefits, you'd have to do more coding than naming a bunch of ints in a new class.. It's subtle though. Not a wild game-changer or anything :)
- e1ven 13y agoThanks.. That makes sense.. And I suppose it also helps satisfy the "explicit is better than implicit" edict, by making it clearer what you are doing.
- pyre 13y agoRead the section on IntEnums[1]: 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] [1] http://www.python.org/dev/peps/pep-0435/#intenum http://www.python.org/dev/peps/pep-0435/#intenum
- dietrichepp 13y agoIn addition to what famousactress said, you also get: >>> Color.red <Color.red: 1> So when you print out an enumeration, you get its name instead of just the number. This can be a big deal for debugging e.g. OpenGL code, which has a lot of enumerations. Which of the following would you prefer? >>> gl.getError() 1286 Or would you prefer, >>> gl.getError() <gl.INVALID_FRAMEBUFFER_OPERATION: 1286>
- nilsbunger 13y agoWhen will this ship in python? I don't know a lot about the typical time between a PEP acceptance and a release containing the feature.
- tshepang 13y agoBefore March next year: http://www.python.org/dev/peps/pep-0429/ http://www.python.org/dev/peps/pep-0429/.
- eliben 13y agoWe expect the implementation will be committed in the next few weeks, and you'll be able to give it a try using Python's tip revision ("trunk"). It will actually _ship_ with Python 3.4
- deleted 13y ago[deleted]
- ajanuary 13y ago> Iterating over the members of an enum does not provide the aliases Anyone know the motivation for this? Seems like a source of frustration to me.
- Tobu 13y agoThe aliases aren't part of the enum, if you think of the enum as a set of values. Iterating is done on the values, not the names. (the next paragraph gives you the __members__ ordered dict, if you want to iterate on something else)
- lmm 13y agoSeems a bit dodgy that enum values can be any value. What happens if you use mutable values and later change them to be the same?
- dvanduzer 13y agoObjects can be mutable or immutable. The value of a mutable object can change to another value, but it's not that the value-in-itself is mutable. http://docs.python.org/3/reference/datamodel.html#objects-values-and-types http://docs.python.org/3/reference/datamodel.html#objects-va...
- lmm 13y agoSo can I declare an enum value to be e.g. a list? Is that value "frozen" when the enum declaration is processed or what?
- dvanduzer 13y agoA list is not a value, it is a type of object. The second to last paragraph of the section in the reference manual I linked may help: Some objects contain references to other objects; these are called containers. Examples of containers are tuples, lists and dictionaries. The references are part of a container’s value. In most cases, when we talk about the value of a container, we imply the values, not the identities of the contained objects; however, when we talk about the mutability of a container, only the identities of the immediately contained objects are implied. So, if an immutable container (like a tuple) contains a reference to a mutable object, its value changes if that mutable object is changed. PEP-435 says enums are bound to "unique, constant values" -- not that you can bind enums to arbitrary types of objects.
- pyre 13y agoYou could say that about a lot of things in Python. If you add a constant value as a class attribute, anyone using that class has the ability to change it, even if that would screw things up.
- nobodysfool 13y agoStupid me, I was using enums the wrong way. I was using an enum like a constant. I.E. If my database takes a 'Sex' parameter, with Male as 1, Female as 2, Unknown as 3, Both as 4 - I'd use an enum like so: update person set sex=Sex.Male Looks like I can't do that with this enum class. Well, I suppose I'd have to do it like so: sex = Sex.Male.value not very sexy...
- bsimpson 13y agoWouldn't IntEnum fit your case better?
- marmaduke 13y agoEnumerations support iteration, in definition order: >>> class Shake(Enum): ... vanilla = 7 ... chocolate = 4 ... cookies = 9 ... mint = 3 ... >>> for shake in Shake: ... print(shake) ... Shake.vanilla Shake.chocolate Shake.cookies Shake.mint Python's execution model sez that the class declaration is nothing more than code that is exec'd in a dictionary. We know that Python dictionaries do not perserve order, so why is this iteration in definition order possible, barring modification to the interpreter or dict type ?
- shoyer 13y agoThere is an OrderedDict class in the collections module of standard library. Enums store their members in a special __members__ attribute, which is such an OrderedDict. See this section of the PEP: http://www.python.org/dev/peps/pep-0435/#duplicating-enum-members-and-values http://www.python.org/dev/peps/pep-0435/#duplicating-enum-me...
- marmaduke 13y agoYou more or less ignored the contents of my comment w.r.t. execution model. I am well aware of OrderedDict, and I still do not understand how this behavior is possible unless Python 3 differs from 2 in the kind of dictionary used in code block executions.
- saidajigumi 13y agoThere's no need to modify the interpreter. Redefining __dict__ to support ordering would be enough, or perhaps defining a purpose-built __metaclass__[1]. [1] http://stackoverflow.com/a/6581949/183481 http://stackoverflow.com/a/6581949/183481
- marmaduke 13y agoNo-go on metaclass, the received type for metaclass __new__ method's members argument is a plain dict. Unless this is changed in Py 3, I still don't understand how this works.
- jjs 13y agoPython has been getting bloated in recent years. Why not just do the following? def enum(*args): return dict(zip(args, range(len(args)))) colors = enum('red', 'green', 'blue') # {'blue': 2, 'green': 1, 'red': 0}
- pavpanchekha 13y agoThere are a few things this proposal does that improves upon yours: + Enum values of different types are incomparable. This is widely seen as a good thing. + Enum values are instances of their Enum; this allows more flexibility in user code. + As a result of the above, you can have a dictionary without Enum values of two different types colliding. + Enum values print in a more friendly way. This is expected to help debugging. + To support the above, enum values know their own name. This is likely helpful both for debugging and for various introspection hacks. + Enums can be iterated over in a fixed order. This allows automated help systems and similar to maintain consistency between runs, improving user experience. + There's a lot more error checking provided to avoid cases like defining the same enum value twice. But I understand your sentiment. We always enjoy smaller languages because it helps us keep them in our heads. But note that Enums aren't a new language feature -- this PEP simply adds another module to the standard library. The code your provide, flawed as it is, is still a pattern common to many different libraries, so it would be good to put it into the stdlib; but if we're doing that, might as well do it right, don't you think?
- jjs 13y agoFair enough. The enums themselves aren't a problem; they just seem symptomatic of a pile-on-more-stuff mentality.
- deleted 13y ago[deleted]
- jayflux 13y ago"Python has been getting bloated in recent years" Didn't Python 3 do more removing/tidying up than actually adding of stuff?
- dschep 13y agoBarry gave a good talk about this at DCPython on Tuesday.