On Sun, Jul 24, 2011 at 3:47 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
We've actually been down the 'namespace object' road years ago (Steve Bethard even wrote a proto-PEP IIRC) and it suffered the same fate as most enum PEPs: everyone has slightly different ideas on what should be supported, so you end up being faced with one of two options: 1. Ignore some of the use cases (so some users still have to do their own thing) 2. Support all of the use cases by increasing the API complexity (so many users will still do their own thing instead of learning the new API)
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum. -- --Guido van Rossum (python.org/~guido)
On Sun, Jul 24, 2011 at 8:33 PM, Guido van Rossum <guido@python.org> wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
I don't care for enums but enough other people do that I wouldn't mind including a blessed implemenation. Warsaw's version is better than all the other enum ad-hockery I've seen. +0 -Jack
On Jul 24, 2011, at 6:19 PM, Jack Diederich wrote:
On Sun, Jul 24, 2011 at 8:33 PM, Guido van Rossum <guido@python.org> wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
I don't care for enums but enough other people do that I wouldn't mind including a blessed implemenation.
I also don't care for enums and recommend against adding them to the language. IMO, it is something that makes good sense in statically compiled languages and is unnecessary for us. Not to mention, we already have several ways to do it (module and class namespaces for example). Also, when this idea came up in the past, it tended to get shot down because the various use cases suggested differing implementations with different features. I also urge caution because Python has already stopped being a small language. Enums are especially problematic because they will pop up everywhere. You won't have the option of ignoring them. Python has achieved an amazing adoption rate without enums. Most people just don't need them. Many, large and clean apps have been built without them. Those that have found did were typically able to implement them easily with the existing toolset. Raymond
On Sun, Jul 24, 2011 at 9:22 PM, Raymond Hettinger <raymond.hettinger@gmail.com> wrote:
On Jul 24, 2011, at 6:19 PM, Jack Diederich wrote:
On Sun, Jul 24, 2011 at 8:33 PM, Guido van Rossum <guido@python.org> wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
I don't care for enums but enough other people do that I wouldn't mind including a blessed implemenation.
I also don't care for enums and recommend against adding them to the language. IMO, it is something that makes good sense in statically compiled languages and is unnecessary for us. Not to mention, we already have several ways to do it (module and class namespaces for example).
Also, when this idea came up in the past, it tended to get shot down because the various use cases suggested differing implementations with different features.
I also urge caution because Python has already stopped being a small language. Enums are especially problematic because they will pop up everywhere. You won't have the option of ignoring them.
Python has achieved an amazing adoption rate without enums. Most people just don't need them. Many, large and clean apps have been built without them. Those that have found did were typically able to implement them easily with the existing toolset.
Don't worry about them being added to the language; it's just a stdlib module I'm proposing here. They do have their uses and fans, or there wouldn't be a PEP and several popular 3rd party libraries. My own reason for sometimes wanting them is that it gets old to use a lookup table from name -> string for printing, and using string values instead of integers isn't always an option. Google's protobuf also has them, as does protorpc, a Pythonic protobuf spinoff by my office mate Rafe Kaplan. -- --Guido van Rossum (python.org/~guido)
On Jul 24, 2011, at 09:22 PM, Raymond Hettinger wrote:
I also don't care for enums and recommend against adding them to the language. IMO, it is something that makes good sense in statically compiled languages and is unnecessary for us. Not to mention, we already have several ways to do it (module and class namespaces for example).
IMHO, enums improve readability and debugging. For better integration with external systems (e.g. databases), enum values are compatible with integers. And this (Pdb) color <EnumValue: Colors.blue [int=3]> is much nicer than (Pdb) color 3 Cheers, -Barry
Raymond Hettinger wrote:
On Jul 24, 2011, at 6:19 PM, Jack Diederich wrote:
On Sun, Jul 24, 2011 at 8:33 PM, Guido van Rossum <guido@python.org> wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
I don't care for enums but enough other people do that I wouldn't mind including a blessed implemenation.
I also don't care for enums and recommend against adding them to the language. IMO, it is something that makes good sense in statically compiled languages and is unnecessary for us. Not to mention, we already have several ways to do it (module and class namespaces for example).
Also, when this idea came up in the past, it tended to get shot down because the various use cases suggested differing implementations with different features.
I also urge caution because Python has already stopped being a small language. Enums are especially problematic because they will pop up everywhere. You won't have the option of ignoring them.
+1
Python has achieved an amazing adoption rate without enums. Most people just don't need them. Many, large and clean apps have been built without them. Those that have found did were typically able to implement them easily with the existing toolset.
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 27 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On 27 July 2011 13:44, M.-A. Lemburg <mal@egenix.com> wrote:
Raymond Hettinger wrote:
On Jul 24, 2011, at 6:19 PM, Jack Diederich wrote:
On Sun, Jul 24, 2011 at 8:33 PM, Guido van Rossum <guido@python.org>
wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
I don't care for enums but enough other people do that I wouldn't mind including a blessed implemenation.
I also don't care for enums and recommend against adding them to the language. IMO, it is something that makes good sense in statically compiled languages and is unnecessary for us. Not to mention, we already have several ways to do it (module and class namespaces for example).
Also, when this idea came up in the past, it tended to get shot down because the various use cases suggested differing implementations with different features.
I also urge caution because Python has already stopped being a small language. Enums are especially problematic because they will pop up everywhere. You won't have the option of ignoring them.
+1
Actually, as they should behave exactly like constants you should entirely be able to "ignore" them (to the same extent you ignore the existing constants anyway). Whether they're exposed at the module level, or grouped in a single namespace (the container), or both, is a stylistic issue for module authors. I've seen plenty of Python modules that group constants as class attributes for convenience even without an enum in the standard library. This proposal just makes those more useful and doesn't add any overhead for *using* them. I think a lot of the objections come from perceived negative experience of Enums in other languages (not particularly aimed at you here MAL). When we discussed this on python-dev several developers were much happier with the proposal if we called them NamedConstants and GroupedConstants rather than Enums. Michael
Python has achieved an amazing adoption rate without enums. Most people just don't need them. Many, large and clean apps have been built without them. Those that have found did were typically able to implement them easily with the existing toolset.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Jul 27 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! ::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
Guido van Rossum <guido@python.org> writes:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
As the maintainer of the PyPI ‘enum’ package, I also support the FLUFL's ‘flufl.enum’ to be the One Obvious Way. -- \ “Odious ideas are not entitled to hide from criticism behind | `\ the human shield of their believers' feelings.” —Richard | _o__) Stallman | Ben Finney
Ben Finney, 25.07.2011 04:09:
Guido van Rossum writes:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
As the maintainer of the PyPI ‘enum’ package, I also support the FLUFL's ‘flufl.enum’ to be the One Obvious Way.
Never used enums in Python, and likely won't do so anywhere in the near future, but I just read through the usage page at http://packages.python.org/flufl.enum/docs/using.html and *if* such a library gets added to the stdlib, then this is the way it should work. The examples totally make sense to me. Stefan
Stefan Behnel wrote:
Ben Finney, 25.07.2011 04:09:
Guido van Rossum writes:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
As the maintainer of the PyPI ‘enum’ package, I also support the FLUFL's ‘flufl.enum’ to be the One Obvious Way.
Never used enums in Python, and likely won't do so anywhere in the near future, but I just read through the usage page at
http://packages.python.org/flufl.enum/docs/using.html
and *if* such a library gets added to the stdlib, then this is the way it should work. The examples totally make sense to me.
Agreed. +1 ~Ethan~
On Sun, Jul 24, 2011 at 11:04 PM, Stefan Behnel <stefan_ml@behnel.de> wrote:
Ben Finney, 25.07.2011 04:09:
Guido van Rossum writes:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
As the maintainer of the PyPI ‘enum’ package, I also support the FLUFL's ‘flufl.enum’ to be the One Obvious Way.
Never used enums in Python, and likely won't do so anywhere in the near future, but I just read through the usage page at
http://packages.python.org/flufl.enum/docs/using.html
and *if* such a library gets added to the stdlib, then this is the way it should work. The examples totally make sense to me.
My only nitpicks would be: - Why violate TOOWTDI by having both SomeEnum(...) and SomeEnum[...] that do the same thing? - Why is it .enumname and not simply .name? .enumname could also be confused as getting the name of the overarching enum rather than the name of the particular enum value. Cheers, Chris
On 25/07/2011 07:32, Chris Rebert wrote:
On Sun, Jul 24, 2011 at 11:04 PM, Stefan Behnel<stefan_ml@behnel.de> wrote:
Ben Finney, 25.07.2011 04:09:
Guido van Rossum writes:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
As the maintainer of the PyPI ‘enum’ package, I also support the FLUFL's ‘flufl.enum’ to be the One Obvious Way.
Never used enums in Python, and likely won't do so anywhere in the near future, but I just read through the usage page at
http://packages.python.org/flufl.enum/docs/using.html
and *if* such a library gets added to the stdlib, then this is the way it should work. The examples totally make sense to me.
My only nitpicks would be: - Why violate TOOWTDI by having both SomeEnum(...) and SomeEnum[...] that do the same thing? - Why is it .enumname and not simply .name? .enumname could also be confused as getting the name of the overarching enum rather than the name of the particular enum value.
My nitpick would be that the underlying integer values should start at 0, not 1.
On Jul 25, 2011, at 05:27 PM, MRAB wrote:
My nitpick would be that the underlying integer values should start at 0, not 1.
That's only relevant for the make_enum() convenience function. For the class syntax, you pick the values. OT1H, make_enum() is *just* a convenience, so if you really want to start from 0, use the class syntax. OTOH, it would probably be trivial to add an `iter` keyword argument to make_enum() from which it could pull the integer sequence to use. That would be better than adding something like range()'s crazy api. :) -Barry
On Jul 24, 2011, at 11:32 PM, Chris Rebert wrote:
- Why violate TOOWTDI by having both SomeEnum(...) and SomeEnum[...] that do the same thing?
I don't remember exactly, but there was some historical reasons for including both syntaxes.
- Why is it .enumname and not simply .name? .enumname could also be confused as getting the name of the overarching enum rather than the name of the particular enum value.
Again, historical accident. Probably .enumname was chosen to mimic .enumclass, since obviously .class couldn't be used. Since these are attributes on enumeration values, .name and .enum might have been better choices. Cheers, -Barry
On 25 July 2011 01:33, Guido van Rossum <guido@python.org> wrote:
We've actually been down the 'namespace object' road years ago (Steve Bethard even wrote a proto-PEP IIRC) and it suffered the same fate as most enum PEPs: everyone has slightly different ideas on what should be supported, so you end up being faced with one of two options: 1. Ignore some of the use cases (so some users still have to do their own
On Sun, Jul 24, 2011 at 3:47 PM, Nick Coghlan <ncoghlan@gmail.com> wrote: thing)
2. Support all of the use cases by increasing the API complexity (so many users will still do their own thing instead of learning the new API)
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
I generally like the flufl.enum API. There are two things it doesn't do. For new apis it is *usually* possible to just use strings rather than integers - and have your library do the mapping if an underlying api takes integers. For existing ones, for example many parts of the python standard library, we couldn't replace the integer values with enums without a lot of effort. If the flufl enums subclassed integer then replacing most of the existing constants in the standard library would be trivially easy (so we'd have a built-in use case). e The second use case, where you really do want to use integers rather than strings, is where you have flag constants that you "or" together. For example in the standard library we have these in r, gzip, msilib, some in xml.dom.NodeFilter, plus a bunch of others. It would be nice to add support for or'ing of enums whilst retaining a nice repr. I did collect a whole lot of emails from a thread last year and was hoping to put a pep together. I'd support flufl.enum - but it would be better if it was extended so we could use it in the standard library. All the best, Michael Foord
-- --Guido van Rossum (python.org/~guido <http://python.org/%7Eguido>) _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On 25 July 2011 17:46, Michael Foord <fuzzyman@gmail.com> wrote:
On 25 July 2011 01:33, Guido van Rossum <guido@python.org> wrote:
On Sun, Jul 24, 2011 at 3:47 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
We've actually been down the 'namespace object' road years ago (Steve Bethard even wrote a proto-PEP IIRC) and it suffered the same fate as most enum PEPs: everyone has slightly different ideas on what should be supported, so you end up being faced with one of two options: 1. Ignore some of the use cases (so some users still have to do their own thing) 2. Support all of the use cases by increasing the API complexity (so many users will still do their own thing instead of learning the new API)
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
I generally like the flufl.enum API. There are two things it doesn't do.
For new apis it is *usually* possible to just use strings rather than integers - and have your library do the mapping if an underlying api takes integers. For existing ones, for example many parts of the python standard library, we couldn't replace the integer values with enums without a lot of effort.
From the thread last year:
Python standard library modules currently using integers for constants: * re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I) * os has SEEK_SET, SEEK_CUR, SEEK_END - **plus** those implemented in posix / nt * doctest has its own flag system, but is really just using integer flags / constants (quite a few of them) * token has a tonne of constants (autogenerated) * socket exports a bunch of constants defined in _socket * gzip has flags: FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT * errno (builtin module) EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED * opcode has HAVE_ARGUMENT, EXTENDED_ARG. In fact pretty much the whole of opcode is about defining and exposing named constants * msilib uses flag constants * multiprocessing.pool - RUN, CLOSE, TERMINATE * multiprocessing.util - NOTSET, SUBDEBUG, DEBUG, INFO, SUBWARNING * xml.dom and xml.dom.Node (in __init__.py) have a bunch of constants * xml.dom.NodeFilter.NodeFilter holds a bunch of constants (some of them flags) * xmlrpc.client has a bunch of error constants * calendar uses constants to represent weekdays, plus one for the EPOCH that is best left alone * http.client has a tonne of constants - recognisable as ports / error codes though * dis has flags in COMPILER_FLAG_NAMES, which are then set as locals in inspect * io defines SEEK_SET, SEEK_CUR, SEEK_END (same as os) Where constants are implemented in C but exported via a Python module (the constants exported by os and socket for example) they could be wrapped. Where they are exported directly by a C extension or builtin module (e.g. errno) they are probably best left. Here's an email from Guido from that thread putting his vote in for enums *as* integers: http://mail.python.org/pipermail/python-dev/2010-November/105916.html All the best, Michael Foord
If the flufl enums subclassed integer then replacing most of the existing constants in the standard library would be trivially easy (so we'd have a built-in use case). e The second use case, where you really do want to use integers rather than strings, is where you have flag constants that you "or" together. For example in the standard library we have these in r, gzip, msilib, some in xml.dom.NodeFilter, plus a bunch of others.
It would be nice to add support for or'ing of enums whilst retaining a nice repr.
I did collect a whole lot of emails from a thread last year and was hoping to put a pep together. I'd support flufl.enum - but it would be better if it was extended so we could use it in the standard library.
All the best,
Michael Foord
--
--Guido van Rossum (python.org/~guido <http://python.org/%7Eguido>) _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
--
May you do good and not evil May you find forgiveness for yourself and forgive others
May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On Jul 25, 2011, at 06:06 PM, Michael Foord wrote:
Here's an email from Guido from that thread putting his vote in for enums *as* integers:
http://mail.python.org/pipermail/python-dev/2010-November/105916.html
Okay, there is that. :) It might be worth doing an example conversion to see what it looks like both with enum values as ints and not as ints, to see if it really makes much of a practical difference. -Barry
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form? If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
* os has SEEK_SET, SEEK_CUR, SEEK_END - *plus* those implemented in posix / nt
Ditto
* doctest has its own flag system, but is really just using integer flags / constants (quite a few of them) * token has a tonne of constants (autogenerated) * socket exports a bunch of constants defined in _socket * gzip has flags: FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT
* errno (builtin module)
EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED
It seems to me that an enum module or class doesn't make any of this code better. AFAICT enums are a fat solution to a thin problem. Since python makes it so easy to put constants in class namespaces and module namespaces, there is much less need for enums than in statically compiled languages. I think the perceived need is really more of a carry-over coding habit than an actual need. That being said, this proposal seems to have momentum, so it is going happen anyway. Once introduced, people will think we've endorsed enums as the one right way to code constants, so we will start to see them everywhere. Expect enums to become ubiquitous. Raymond
On 25 July 2011 19:47, Raymond Hettinger <raymond.hettinger@gmail.com>wrote:
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form?
If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
Nope. Just something like this at the module level. IGNORECASE = Flags.IGNORECASE What it gains you is a nicer representation when looking at the values when debugging. On the other hand being able to have access to all the constants namespaced on a single object (as well) doesn't seem like such a bad thing. Michael
* os has SEEK_SET, SEEK_CUR, SEEK_END - **plus** those implemented in posix / nt
Ditto
* doctest has its own flag system, but is really just using integer flags / constants (quite a few of them) * token has a tonne of constants (autogenerated) * socket exports a bunch of constants defined in _socket * gzip has flags: FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT
* errno (builtin module)
EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED
It seems to me that an enum module or class doesn't make any of this code better. AFAICT enums are a fat solution to a thin problem.
Since python makes it so easy to put constants in class namespaces and module namespaces, there is much less need for enums than in statically compiled languages. I think the perceived need is really more of a carry-over coding habit than an actual need.
That being said, this proposal seems to have momentum, so it is going happen anyway. Once introduced, people will think we've endorsed enums as the one right way to code constants, so we will start to see them everywhere. Expect enums to become ubiquitous.
Raymond
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On 2011-07-25, at 21:09 , Michael Foord wrote:
On 25 July 2011 19:47, Raymond Hettinger <raymond.hettinger@gmail.com>wrote:
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form?
If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
Nope. Just something like this at the module level.
IGNORECASE = Flags.IGNORECASE
What it gains you is a nicer representation when looking at the values when debugging. On the other hand being able to have access to all the constants namespaced on a single object (as well) doesn't seem like such a bad thing. Especially nice with unions/a good syntax for enumset. Because "74" being re.IGNORECASE | re.MULITILINE | re.VERBOSE is not exactly obvious.
Whereas an str like <Enumset: {Flags.ignorecase, Flags.multiline, Flags.verbose} [int=74]> could be rather nice (not overly fond of automatically OR-ing ints for unions/sets, though, and thinking that there could be value in "enum values" being singletons rather than values)
On 7/25/11 2:09 PM, Michael Foord wrote:
On 25 July 2011 19:47, Raymond Hettinger <raymond.hettinger@gmail.com <mailto:raymond.hettinger@gmail.com>> wrote:
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form?
If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
Nope. Just something like this at the module level.
IGNORECASE = Flags.IGNORECASE
What it gains you is a nicer representation when looking at the values when debugging.
+1. It's not a problem I run into every time I debug something, but it happens often enough that the feeling of frustration is quite familiar each time it crops up. It's like an old college acquaintance that shows up to crash "just a few nights" on your couch every time he's in town. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
On 25 July 2011 21:33, Robert Kern <robert.kern@gmail.com> wrote:
On 7/25/11 2:09 PM, Michael Foord wrote:
On 25 July 2011 19:47, Raymond Hettinger <raymond.hettinger@gmail.com <mailto:raymond.hettinger@**gmail.com <raymond.hettinger@gmail.com>>> wrote:
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form?
If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
Nope. Just something like this at the module level.
IGNORECASE = Flags.IGNORECASE
What it gains you is a nicer representation when looking at the values when debugging.
+1. It's not a problem I run into every time I debug something, but it happens often enough that the feeling of frustration is quite familiar each time it crops up. It's like an old college acquaintance that shows up to crash "just a few nights" on your couch every time he's in town.
-- Robert Kern
"I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
______________________________**_________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/**mailman/listinfo/python-ideas<http://mail.python.org/mailman/listinfo/python-ideas>
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
2011/7/25 Michael Foord <fuzzyman@gmail.com>
On 25 July 2011 19:47, Raymond Hettinger <raymond.hettinger@gmail.com>wrote:
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form?
If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
Nope. Just something like this at the module level.
IGNORECASE = Flags.IGNORECASE
What it gains you is a nicer representation when looking at the values when debugging. On the other hand being able to have access to all the constants namespaced on a single object (as well) doesn't seem like such a bad thing.
Having aliases all around stdlib module namespaces doesn't sound like a good idea to me at all. "There should be one and preferably one obvious way to do it". That aside, it's not clear what name convention to use that extra "Flags" namespace which wasn't there before. Why re.Flags.* instead of re.Constants.*? If we decide to use re.Flags what should we expect to find into the socket module? socket.Constants? If so, why do we find AF_INET in socket.Constants.AF_INET rather than socket.Family.AF_INET? ...and so on. Also, while before it was clear that module.SOMETHING was referring to a constant, now I that I make a dir() of a module and see "Something" instead of "SOMETHING", the first thing which comes to my mind is that "Something" is a class, not a container of some constants. Regards, --- Giampaolo http://code.google.com/p/pyftpdlib/<http://code.google.com/p/psutil/downloads/list> http://code.google.com/p/psutil/<http://code.google.com/p/psutil/downloads/list>
On 27 July 2011 13:36, Giampaolo Rodolà <g.rodola@gmail.com> wrote:
2011/7/25 Michael Foord <fuzzyman@gmail.com>
On 25 July 2011 19:47, Raymond Hettinger <raymond.hettinger@gmail.com>wrote:
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form?
If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
Nope. Just something like this at the module level.
IGNORECASE = Flags.IGNORECASE
What it gains you is a nicer representation when looking at the values when debugging. On the other hand being able to have access to all the constants namespaced on a single object (as well) doesn't seem like such a bad thing.
Having aliases all around stdlib module namespaces doesn't sound like a good idea to me at all. "There should be one and preferably one obvious way to do it". That aside, it's not clear what name convention to use that extra "Flags" namespace which wasn't there before.
Why re.Flags.* instead of re.Constants.*?
I used Flags as an arbitrary name for an example. It could be anything. It could be _Constants if it was decided that the constant group itself wasn't useful. I would expect it to be useful.
If we decide to use re.Flags what should we expect to find into the socket module? socket.Constants? If so, why do we find AF_INET in socket.Constants.AF_INET rather than socket.Family.AF_INET?
...and so on.
We can give it whatever name is most useful.
Also, while before it was clear that module.SOMETHING was referring to a constant, now I that I make a dir() of a module and see "Something" instead of "SOMETHING", the first thing which comes to my mind is that "Something" is a class, not a container of some constants.
Something would be a class. SOMETHING would still exist and should behave as expected. "Something" should in fact have a name that *does* tell you what it does. "Constants" for example gives you a good clue. Michael
Regards,
--- Giampaolo http://code.google.com/p/pyftpdlib/<http://code.google.com/p/psutil/downloads/list> http://code.google.com/p/psutil/<http://code.google.com/p/psutil/downloads/list>
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On Mon, Jul 25, 2011 at 9:47 PM, Raymond Hettinger <raymond.hettinger@gmail.com> wrote:
On Jul 25, 2011, at 1:06 PM, Michael Foord wrote:
Python standard library modules currently using integers for constants:
* re - has flags (OR'able constants) defined in sre_constants, each flag has two names (e.g. re.IGNORECASE and re.I)
What is being proposed here? Will there be a new namespace so that we would start writing re.Flags.IGNORECASE instead of re.IGNORECASE? Are module level constants now going to be considered bad-form? If constants switch from re.IGNORECASE to re.flags.IGNORECASE, are there any benefits to compensate for being both wordier and slower?
Well, I'll be happy to write: from re import compile as re, Flags as ref (not sure exact names, but you've got the idea) -- Paul
On Jul 25, 2011, at 05:46 PM, Michael Foord wrote:
For new apis it is *usually* possible to just use strings rather than integers - and have your library do the mapping if an underlying api takes integers. For existing ones, for example many parts of the python standard library, we couldn't replace the integer values with enums without a lot of effort.
If the flufl enums subclassed integer then replacing most of the existing constants in the standard library would be trivially easy (so we'd have a built-in use case).
I'm not sure it would be a good idea to do a mass mechanical substitution in the stdlib, so I'm not concerned that adopting enums would require a few int() calls to be added. I've so far resisted the occasional suggestion to make enum values int subclasses, because I think enum values should not *be* ints, but should be compatible with ints (as they are now). Ordered comparisons of enum values bothers me. ;)
The second use case, where you really do want to use integers rather than strings, is where you have flag constants that you "or" together. For example in the standard library we have these in r, gzip, msilib, some in xml.dom.NodeFilter, plus a bunch of others.
It would be nice to add support for or'ing of enums whilst retaining a nice repr.
Hmm. Of course ORing the enum values' int representation is trivial; you just have to pick the right values when you create the class. I'd be interested to see some specific examples for something more direct you have in mind. (Perhaps as a merge proposal <wink>.)
I did collect a whole lot of emails from a thread last year and was hoping to put a pep together. I'd support flufl.enum - but it would be better if it was extended so we could use it in the standard library.
I think it would be usable in the stdlib today, but I'd like to see specific examples of how you think the API needs to be changed. Making enum values ints and supporting ordered comparisons is a big difference IMO. Cheers, -Barry
On 25 July 2011 18:34, Barry Warsaw <barry@python.org> wrote:
On Jul 25, 2011, at 05:46 PM, Michael Foord wrote:
For new apis it is *usually* possible to just use strings rather than integers - and have your library do the mapping if an underlying api takes integers. For existing ones, for example many parts of the python standard library, we couldn't replace the integer values with enums without a lot of effort.
If the flufl enums subclassed integer then replacing most of the existing constants in the standard library would be trivially easy (so we'd have a built-in use case).
I'm not sure it would be a good idea to do a mass mechanical substitution in the stdlib, so I'm not concerned that adopting enums would require a few int() calls to be added.
Well, if it isn't good enough for *us* then who is it good enough for? ;-) Some apis (for example those exported directly from C) can't work with something that isn't a real int.
I've so far resisted the occasional suggestion to make enum values int subclasses, because I think enum values should not *be* ints, but should be compatible with ints (as they are now). Ordered comparisons of enum values bothers me. ;)
The second use case, where you really do want to use integers rather than strings, is where you have flag constants that you "or" together. For example in the standard library we have these in r, gzip, msilib, some in xml.dom.NodeFilter, plus a bunch of others.
It would be nice to add support for or'ing of enums whilst retaining a nice repr.
Hmm. Of course ORing the enum values' int representation is trivial; you just have to pick the right values when you create the class. I'd be interested to see some specific examples for something more direct you have in mind. (Perhaps as a merge proposal <wink>.)
Well:
from flufl.enum import Enum class Thing(Enum): ... a = 1 ... b = 2 ... c = 4 ... Thing.a | Thing.b Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'EnumValue' and 'EnumValue'
I did collect a whole lot of emails from a thread last year and was hoping to put a pep together. I'd support flufl.enum - but it would be better if it was extended so we could use it in the standard library.
I think it would be usable in the stdlib today, but I'd like to see specific examples of how you think the API needs to be changed. Making enum values ints and supporting ordered comparisons is a big difference IMO.
Sure, I have no use case for ordering enums... I'm not sure I have a specific reason to *prevent* if that is extra work though. Michael
Cheers, -Barry
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On Jul 25, 2011, at 07:43 PM, Michael Foord wrote:
On 25 July 2011 18:34, Barry Warsaw <barry@python.org> wrote:
I'm not sure it would be a good idea to do a mass mechanical substitution in the stdlib, so I'm not concerned that adopting enums would require a few int() calls to be added.
Well, if it isn't good enough for *us* then who is it good enough for? ;-)
I only meant that we have a tradition of not doing wholesale mechanical conversions when new features are added to the language or modules to the stdlib. This wouldn't be any different. :)
Some apis (for example those exported directly from C) can't work with something that isn't a real int.
That might be an important enough use case to sway me!
from flufl.enum import Enum class Thing(Enum): ... a = 1 ... b = 2 ... c = 4 ... Thing.a | Thing.b Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'EnumValue' and 'EnumValue'
Right. That's a consequence I think of EnumValues not being ints (and no other support for such operations being added.
Sure, I have no use case for ordering enums... I'm not sure I have a specific reason to *prevent* if that is extra work though.
Possibly so. I think my aversion to it is seeing things like >>> if Colors.red < Colors.blue: or even >>> if color < Colors.blue: Most enums (at least IME) are discrete objects that don't have a natural ordering. -Barry
Barry Warsaw <barry@python.org> writes:
Most enums (at least IME) are discrete objects that don't have a natural ordering.
+1. This is an important property of an enumerated type, IMO. The fact that ‘bool’ disagrees (‘False < True’) is an artifact of its history, and is not relevant for an argument for orderable EnumValues. -- \ “A thing moderately good is not so good as it ought to be. | `\ Moderation in temper is always a virtue; but moderation in | _o__) principle is always a vice.” —Thomas Paine | Ben Finney
Ben Finney wrote:
Barry Warsaw <barry@python.org> writes:
Most enums (at least IME) are discrete objects that don't have a natural ordering.
+1. This is an important property of an enumerated type, IMO.
In most cases I agree, but there can be exceptions. Consider: class StoveTemps(Enum): off = 0 low = 15 medium = 50 high = 85 In such a case I see no issues with if temp < StoveTemps.medium: what_ever() ~Ethan~
Ethan Furman <ethan@stoneleaf.us> writes:
In most cases I agree, but there can be exceptions. Consider:
class StoveTemps(Enum): off = 0 low = 15 medium = 50 high = 85
That doesn't seem like an appropriate use of an enumerated type, then. For an enumerated type, the integers are arbitrary and the important part is the name. If the integer values actually *mean* something, then let's have them as name bindings to those values.
In such a case I see no issues with
if temp < StoveTemps.medium: what_ever()
-- \ “Anger makes dull men witty, but it keeps them poor.” | `\ —Elizabeth Tudor | _o__) | Ben Finney
Ben Finney wrote:
Barry Warsaw <barry@python.org> writes:
Most enums (at least IME) are discrete objects that don't have a natural ordering.
+1. This is an important property of an enumerated type, IMO.
If you mean that it's important for an enum *not* to have an ordering, I disagree. There are some use cases that benefit from having an ordering, and some that don't. For those that don't, personally I don't care whether it has an ordering or not. Raising an exception on ordered comparison could perhaps be an optional feature, but I wouldn't like it to be the only option. -- Greg
On Thu, 28 Jul 2011 13:45:54 +1200 Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Ben Finney wrote:
Barry Warsaw <barry@python.org> writes:
Most enums (at least IME) are discrete objects that don't have a natural ordering.
+1. This is an important property of an enumerated type, IMO.
If you mean that it's important for an enum *not* to have an ordering, I disagree. There are some use cases that benefit from having an ordering, and some that don't. For those that don't, personally I don't care whether it has an ordering or not.
Question: how does iterating over the group fit into this? For some enums (colors, for instance) order may be irrelevant or an implementation detail, but you'd still like to be able to iterate over the group. Not sure it matters, but thought it might. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
Mike Meyer <mwm@mired.org> writes:
Question: how does iterating over the group fit into this? For some enums (colors, for instance) order may be irrelevant or an implementation detail, but you'd still like to be able to iterate over the group.
You can iterate over members of a set, too. What's the question? -- \ “To have the choice between proprietary software packages, is | `\ being able to choose your master. Freedom means not having a | _o__) master.” —Richard M. Stallman, 2007-05-16 | Ben Finney
On Jul 28, 2011, at 01:45 PM, Greg Ewing wrote:
If you mean that it's important for an enum *not* to have an ordering, I disagree. There are some use cases that benefit from having an ordering, and some that don't. For those that don't, personally I don't care whether it has an ordering or not.
Based on my recent experiment (see my other follow up), values-as-ints can have unintended side-effects. It's clear that making this change would be backward incompatible to my existing library, and I think it indicates that there are indeed separate use cases for ordered enums and unordered enums. I think if I were to accept values-as-ints as a new feature for my library, I'd want to make it a separate class. Yes, it complicates the library but maybe the two design requirements are just incompatible. -Barry
On Tue, Jul 26, 2011 at 5:18 AM, Barry Warsaw <barry@python.org> wrote:
Most enums (at least IME) are discrete objects that don't have a natural ordering.
For compatibility reasons, though, I believe it is worth following the precedent set by bool: if integer constants are to be replaced by named values, the replacement needs to support everything the previous value did. The easiest way to achieve that is by inheriting directly from the previous value's type. I'm getting deja vu as I formulate this next bit, so I suspect I've said similar things in the past... 1. I find the "enum" concept too limiting. NamedValue (as a mixin class) is a better building block since it separates the naming aspect from the "group of values" aspect implied by enum. It would be nice, for example, if we could do things like "tau = NamedFloat('tau', 2*math.pi)" where NamedFloat is essentially just "class NamedFloat(NamedValue, float): pass" (this could, of course, be hidden behind a factory function that used type(value) and a cache to dynamically create appropriate subclasses) 2. All the stdlib use cases have a relevant namespace already (the module level one). They are better served by a namedtuple style solution that adds naming behaviour at the object level without trying to organise groups of named constants in any particular fashion. 3. The named value approach can improve the ease of debugging *any* significant data structure, not just constants. 4. If named values are available as a building block, they can be used by any enum implementation. In and of themselves they wouldn't inherit from any particular type, so enum implementations could decide which types to support and how comparisons and other operations should behave. As Raymond says, clearly none of this is *necessary*. However, the usefulness of objects that know their official name has proven itself extensively in the form of classes and functions, and collections.namedtuple is a recent addition that has proven similarly beneficial for improving the maintainability of tuples by providing additional static metadata. Extending that kind of benefit to arbitrary instances has clear utility without significantly impacting compatibility (since we would be substituting instances of a class with instances of subclasses that only modify display related behaviour). But the grouping behaviour? I really don't see a lot of value in that - just boilerplate. We don't write bool.True and bool.False, we write True and False. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Wed, Jul 27, 2011 at 3:24 PM, Nick Coghlan <ncoghlan@gmail.com> wrote: [...]
But the grouping behaviour? I really don't see a lot of value in that - just boilerplate. We don't write bool.True and bool.False, we write True and False.
I have to agree with that. Enums can live in a module (together with stuff that uses them) or in a class (if all the uses are limited to that class); they don't need an extra namespace of their own. The standard examples using homonyms in unrelated enums are typically missing the fact that Python doesn't really have *global* variables -- only "module-global", which is really quite narrowly scoped. If a single module or package is exporting so many enums that extra scoping is considered useful, it's easy enough to introduce an extra namespace using existing mechanisms (e.g. a class or a submodule). -- --Guido van Rossum (python.org/~guido)
On Jul 28, 2011, at 08:24 AM, Nick Coghlan wrote:
For compatibility reasons, though, I believe it is worth following the precedent set by bool: if integer constants are to be replaced by named values, the replacement needs to support everything the previous value did. The easiest way to achieve that is by inheriting directly from the previous value's type.
I mentioned in a separate follow up that I'd be open to that, if someone wants to submit a patch.
1. I find the "enum" concept too limiting. NamedValue (as a mixin class) is a better building block since it separates the naming aspect from the "group of values" aspect implied by enum.
I think that's all fine, but it's getting far from *my* simple concept of enumerated values. Go for it though. :)
But the grouping behaviour? I really don't see a lot of value in that - just boilerplate. We don't write bool.True and bool.False, we write True and False.
That's only because True and False are bound in a particular module namespace. There's no reason why specific EnumValues couldn't also be similarly bound. -Barry
On Thu, Jul 28, 2011 at 9:00 AM, Barry Warsaw <barry@python.org> wrote:
On Jul 28, 2011, at 08:24 AM, Nick Coghlan wrote:
For compatibility reasons, though, I believe it is worth following the precedent set by bool: if integer constants are to be replaced by named values, the replacement needs to support everything the previous value did. The easiest way to achieve that is by inheriting directly from the previous value's type.
I mentioned in a separate follow up that I'd be open to that, if someone wants to submit a patch.
I did see that, but I don't see a straighforward way to do it without locking the enum implementation into being *specifically* for integers.
1. I find the "enum" concept too limiting. NamedValue (as a mixin class) is a better building block since it separates the naming aspect from the "group of values" aspect implied by enum.
I think that's all fine, but it's getting far from *my* simple concept of enumerated values. Go for it though. :)
How is something that does *less* more complicated? This recipe is pretty much the full extent of the proposal (perhaps with the automatic type generation I mention in the discussion section): http://code.activestate.com/recipes/577810-named-values/
But the grouping behaviour? I really don't see a lot of value in that - just boilerplate. We don't write bool.True and bool.False, we write True and False.
That's only because True and False are bound in a particular module namespace. There's no reason why specific EnumValues couldn't also be similarly bound.
But the *necessity* of grouping creates a lot of boilerplate and prevents TOOWTDI. The obvious way to do constants in current Python is as module level constants. The obvious way to do enums, on the other hand, is to have a grouping class that holds the names. Translating between the two requires a boilerplate loop over the enum values to add references into the module namespace. Whereas, with the named value approach, all you *have* to do is change "named_constant = x" to "named_constant = named_value('named_constant', x)". The various enum libraries could then become convenience APIs to make it easier to create groups of named values. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Jul 28, 2011, at 10:30 AM, Nick Coghlan wrote:
I did see that, but I don't see a straighforward way to do it without locking the enum implementation into being *specifically* for integers.
Which is fine by me, FWIW.
1. I find the "enum" concept too limiting. NamedValue (as a mixin class) is a better building block since it separates the naming aspect from the "group of values" aspect implied by enum.
I think that's all fine, but it's getting far from *my* simple concept of enumerated values. Go for it though. :)
How is something that does *less* more complicated?
Again, looking at how I've used them extensively over the last several years, I would much rather write class Colors(Enum): red = 1 green = 2 blue = 3 than red = NamedValue('red', 1) green = NamedValue('green', 2) blue = NamedValue('blue', 3) To me, the duplication is jarring and error prone.
But the *necessity* of grouping creates a lot of boilerplate and prevents TOOWTDI.
I have no problem if folks want to add named values to the stdlib. I use namedtuple on occasion, and I wouldn't mind having named values in my arsenal, but I'm fairly certain I wouldn't use named values in the places I use Enum currently. The grouping does provide benefits such as disallowing duplicates, providing for an inheritance mechanism, and making them easier to pickle and store in (and load from) a database. I didn't original think of using enums as a replacement for module level constants, so I think we're just talking about different use cases. -Barry
On Thu, Jul 28, 2011 at 10:56 AM, Barry Warsaw <barry@python.org> wrote:
Again, looking at how I've used them extensively over the last several years, I would much rather write
class Colors(Enum): red = 1 green = 2 blue = 3
than
red = NamedValue('red', 1) green = NamedValue('green', 2) blue = NamedValue('blue', 3)
To me, the duplication is jarring and error prone.
Yeah, I'd actually be inclined to define such values programmatically rather than writing them out manually like that: _named_colours = dict( red=0xFF0000, green=0x00FF00, blue=0x0000FF, ) globals().update((k, namedvalue(k, v)) for k, v in _named_colours) (where namedvalue is the value based factory function I mentioned in the recipe post) However, my contention is that the fundamentally interesting operation is associating names with values (as your EnumValue class does). Enums and their ilk are then just syntactic sugar for defining groups of such values without needing to repeat yourself. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Jul 28, 2011, at 11:12 AM, Nick Coghlan wrote:
However, my contention is that the fundamentally interesting operation is associating names with values (as your EnumValue class does).
That's *an* interesting part, but for my uses not the only ones. But now I'm repeating myself. :) -Barry
On Wed, Jul 27, 2011 at 6:12 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Thu, Jul 28, 2011 at 10:56 AM, Barry Warsaw <barry@python.org> wrote:
Again, looking at how I've used them extensively over the last several years, I would much rather write
class Colors(Enum): red = 1 green = 2 blue = 3
than
red = NamedValue('red', 1) green = NamedValue('green', 2) blue = NamedValue('blue', 3)
To me, the duplication is jarring and error prone.
Agreed, especially in new code, when you just want to get the names defined, and you don't have to worry about compatibility. I think that's why NamedValue would often be wrapped by an enum class. But there is the downside that, for all the elegance of writing class Color(enum): red = 1 ... all the *uses* of colors have to write foo.Color.red instead of foo.red.
Yeah, I'd actually be inclined to define such values programmatically rather than writing them out manually like that:
_named_colours = dict( red=0xFF0000, green=0x00FF00, blue=0x0000FF, ) globals().update((k, namedvalue(k, v)) for k, v in _named_colours)
Eek, no! It will take the average reader way too long to figure out that that does. Static analyzers (which are getting more important) are likely to be fooled by it too. Please remember EIBTI.
(where namedvalue is the value based factory function I mentioned in the recipe post)
However, my contention is that the fundamentally interesting operation is associating names with values (as your EnumValue class does). Enums and their ilk are then just syntactic sugar for defining groups of such values without needing to repeat yourself.
It's just possible that there's no way to define enums that neither introduced a new (class) scope nor requires a lot of redundant typing. I wish I could write red = Enum(1) and it made the following true: assert red == 1 assert isinstance(red, int) # a subclass assert str(red) == 'red' But we'd first need a non-hacky way for Enum() to know that it is being assigned to something named 'red'. I have a few other use cases for that as well, e.g. a Property class for App Engine that doesn't require a metaclass to patch up the value. A half-solution would be if naturally the definition of red appeared inside some other class (not a class specially created for the enum, but a class that has other functionality) and somehow a mix-in metaclass (if such a beast exists) would pass in the name once the class is being defined. But if you want module-level enum values you'd still have to do something like class Color(Enum): red = 1 ... red, green, blue = Color.red, Color.green, Color.blue That's not ideal since the next programmer could add a new color but forget to also pull it into the outer scope. And no, I don't like solutions based on globals()... TBH, I'm not sure we should hold our breath until we have the perfect solution. Looking over flufl.enum again, I like most of its design decisions except the "enums are not integers" part. For me, after the above definition of class Color, I'd be happy of Color.red == 1, as long as str(Color.red) == 'red'. (Here I am consistent with the behavior of True and False.) If you want the enum values to appear at the module level (e.g. socket constants?) pulling them up a level explicitly, while not ideal (see above), is not the end of the world either. -- --Guido van Rossum (python.org/~guido)
On Thu, Jul 28, 2011 at 12:21 PM, Guido van Rossum <guido@python.org> wrote:
I wish I could write
red = Enum(1)
and it made the following true:
assert red == 1 assert isinstance(red, int) # a subclass assert str(red) == 'red'
But we'd first need a non-hacky way for Enum() to know that it is being assigned to something named 'red'. I have a few other use cases for that as well, e.g. a Property class for App Engine that doesn't require a metaclass to patch up the value.
Yeah, a similar discussion came up in the context of defining namedtuple instances a while back (I don't recall if you were part of that conversation or not - IIRC, the thread started off on the topic of assignment decorators and ended up wandering down this road at some point) Most proposed solutions relied on some form of abuse of the def statement to define arbitrary objects that knew their own name. For example: def red from namedvalue(1) # Rather unnatural phrasing def red as namedvalue(1) # Phrasing is natural, but the name is on the wrong side of the 'as' def red = namedvalue(1) # Simple assignment may not suggest enough magic def as red = namedvalue(1) # Syntax soup! as red def namedvalue(1) # Just throw keywords at the screen and see if anything sticks red def= namedvalue(1) # An alternative inspired by augmented assignment def red << namedvalue(1) # Arbitrary but suggestive A protocol would then be defined to make that work (regardless of the specific syntax). Either the actual call would be transformed into "namedvalue('red', 1)" (which has the virtue of working with existing objects like collections.namedtuple, but prevents provision of useful default behaviour) or else there would be a new protocol like "obj.__named__(name)", with a fallback to something like the NamedValue recipe if __named__ wasn't defined (which has the virtue of working with arbitrary objects as in "def red as 1", but requires adapter classes to work with existing APIs like namedtuple). The bikeshed was painted many different colours before the thread finally wound down without achieving any kind of resolution. I suspect this may end up being another PEP 308, where the only way it will ever happen is if *you* find a syntax you like (or are at least willing to tolerate in order to gain the functionality). Adding an int-specific Enum class eliminates a lot of the use cases for a "named object" feature, which is the main reason I'm trying to hold out for the more general functionality - I still have hope that we'll strike on a syntax for named assignments that is clear enough and clean enough that none of the core devs actively oppose it. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 28/07/2011 04:57, Nick Coghlan wrote:
On Thu, Jul 28, 2011 at 12:21 PM, Guido van Rossum<guido@python.org> wrote:
I wish I could write
red = Enum(1)
and it made the following true:
assert red == 1 assert isinstance(red, int) # a subclass assert str(red) == 'red'
But we'd first need a non-hacky way for Enum() to know that it is being assigned to something named 'red'. I have a few other use cases for that as well, e.g. a Property class for App Engine that doesn't require a metaclass to patch up the value.
Yeah, a similar discussion came up in the context of defining namedtuple instances a while back (I don't recall if you were part of that conversation or not - IIRC, the thread started off on the topic of assignment decorators and ended up wandering down this road at some point)
Most proposed solutions relied on some form of abuse of the def statement to define arbitrary objects that knew their own name. For example:
def red from namedvalue(1) # Rather unnatural phrasing def red as namedvalue(1) # Phrasing is natural, but the name is on the wrong side of the 'as' def red = namedvalue(1) # Simple assignment may not suggest enough magic def as red = namedvalue(1) # Syntax soup! as red def namedvalue(1) # Just throw keywords at the screen and see if anything sticks red def= namedvalue(1) # An alternative inspired by augmented assignment def red<< namedvalue(1) # Arbitrary but suggestive
[snip] You missed out: def red is namedvalue(1)
On 28 July 2011 14:52, MRAB <python@mrabarnett.plus.com> wrote:
On 28/07/2011 04:57, Nick Coghlan wrote:
Most proposed solutions relied on some form of abuse of the def statement to define arbitrary objects that knew their own name. For example:
def red from namedvalue(1) # Rather unnatural phrasing def red as namedvalue(1) # Phrasing is natural, but the name is on the wrong side of the 'as' def red = namedvalue(1) # Simple assignment may not suggest enough magic def as red = namedvalue(1) # Syntax soup! as red def namedvalue(1) # Just throw keywords at the screen and see if anything sticks red def= namedvalue(1) # An alternative inspired by augmented assignment def red<< namedvalue(1) # Arbitrary but suggestive
[snip] You missed out:
def red is namedvalue(1)
IIRC, there was also red := namedvalue(1) which is probably disallowed because it looks like grit on Tim's screen, but actually has some relevant history in that := is used as an assignment operator in many languages. Paul
On Wed, Jul 27, 2011 at 8:57 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Thu, Jul 28, 2011 at 12:21 PM, Guido van Rossum <guido@python.org> wrote:
I wish I could write
red = Enum(1)
and it made the following true:
assert red == 1 assert isinstance(red, int) # a subclass assert str(red) == 'red'
But we'd first need a non-hacky way for Enum() to know that it is being assigned to something named 'red'. I have a few other use cases for that as well, e.g. a Property class for App Engine that doesn't require a metaclass to patch up the value.
Yeah, a similar discussion came up in the context of defining namedtuple instances a while back (I don't recall if you were part of that conversation or not - IIRC, the thread started off on the topic of assignment decorators and ended up wandering down this road at some point)
I recall that, and I think I even contributed. But I agree we never came up with anything acceptable.
Most proposed solutions relied on some form of abuse of the def statement to define arbitrary objects that knew their own name. For example:
def red from namedvalue(1) # Rather unnatural phrasing def red as namedvalue(1) # Phrasing is natural, but the name is on the wrong side of the 'as' def red = namedvalue(1) # Simple assignment may not suggest enough magic def as red = namedvalue(1) # Syntax soup! as red def namedvalue(1) # Just throw keywords at the screen and see if anything sticks red def= namedvalue(1) # An alternative inspired by augmented assignment def red << namedvalue(1) # Arbitrary but suggestive
A protocol would then be defined to make that work (regardless of the specific syntax). Either the actual call would be transformed into "namedvalue('red', 1)" (which has the virtue of working with existing objects like collections.namedtuple, but prevents provision of useful default behaviour) or else there would be a new protocol like "obj.__named__(name)", with a fallback to something like the NamedValue recipe if __named__ wasn't defined (which has the virtue of working with arbitrary objects as in "def red as 1", but requires adapter classes to work with existing APIs like namedtuple).
The bikeshed was painted many different colours before the thread finally wound down without achieving any kind of resolution. I suspect this may end up being another PEP 308, where the only way it will ever happen is if *you* find a syntax you like (or are at least willing to tolerate in order to gain the functionality).
My current thinking would be that you could probably hack it in CPython through bytecode inspection: find the STORE_FAST or STORE_GLOBAL opcode right after the CALL_FUNCTION opcode, and see what to name it assigns. This is of course terrible, depending on all sorts of implementation stuff, but we might create a builtin function that returns that variable name, assuming the scope is module-global or a class scope. I know it sounds atrocious, but the alternative is requiring extra mark-up and a hidden implied argument, which does not sound good either. (Maybe extra mark-up could be required for the proposed new builtin to work.)
Adding an int-specific Enum class eliminates a lot of the use cases for a "named object" feature, which is the main reason I'm trying to hold out for the more general functionality - I still have hope that we'll strike on a syntax for named assignments that is clear enough and clean enough that none of the core devs actively oppose it.
I agree that named constants don't have to be ints -- though for most types other than ints, my main requirement for enums (that they print themselves nicely) is not an issue. Regarding Ben Finney's requirement that enums primarily define unique values and that enums from two different classes must compare unequal even if their int values are equal, I really don't care about that. You can use isinstance() if you want to check that. -- --Guido van Rossum (python.org/~guido)
Guido van Rossum <guido@python.org> writes:
TBH, I'm not sure we should hold our breath until we have the perfect solution.
That's an important principle, certainly.
Looking over flufl.enum again, I like most of its design decisions except the "enums are not integers" part. For me, after the above definition of class Color, I'd be happy of Color.red == 1, as long as str(Color.red) == 'red'.
I am displeased if we don't get this:: >>> Color.red == 1 False >>> Fruit.tomato == 1 False but wouldn't want to block an ‘enum’ implementation waiting for that. What I'd hold out for, though, is:: >>> Color.red == Fruit.tomato False That is, all of the values from Color should compare as inequal with any other value. This is one of the main features to want from an enumerated type, IMO: to have a set of values that are distinct from any other value, that won't be accidentally equal to any other value, and have helpful string representations.
(Here I am consistent with the behavior of True and False.)
Do you see that (the behaviour of True and False comparing equal with integers) as anything more than backward-compatible baggage? To me, if I had the time machine, a proper representation of a boolean type would have True and False as distinct values, never comparing equal with any other value. -- \ “Any intelligent fool can make things bigger and more complex… | `\ It takes a touch of genius – and a lot of courage – to move in | _o__) the opposite direction.” —Albert Einstein | Ben Finney
On Jul 28, 2011, at 02:49 PM, Ben Finney wrote:
What I'd hold out for, though, is::
Color.red == Fruit.tomato False
That is, all of the values from Color should compare as inequal with any other value.
Does it have to be an equality test, or is identity tests enough?
This is one of the main features to want from an enumerated type, IMO: to have a set of values that are distinct from any other value, that won't be accidentally equal to any other value, and have helpful string representations.
(Here I am consistent with the behavior of True and False.)
Do you see that (the behaviour of True and False comparing equal with integers) as anything more than backward-compatible baggage?
To me, if I had the time machine, a proper representation of a boolean type would have True and False as distinct values, never comparing equal with any other value.
Except possibly for ancient modules, does anybody still actually use the int-iness of bools explicitly? -Barry
On Thu, Jul 28, 2011 at 10:27 AM, Barry Warsaw <barry@python.org> wrote:
Except possibly for ancient modules, does anybody still actually use the int-iness of bools explicitly?
I do, but maybe I myself qualify as an ancient module. :-) The bool-ness of ints is still used widely of course... -- --Guido van Rossum (python.org/~guido)
On Jul 28, 2011, at 10:42 AM, Guido van Rossum wrote:
I do, but maybe I myself qualify as an ancient module. :-)
from __future__ import guido File "<stdin>", line 1 SyntaxError: future feature guido is not defined
Even more troubling though is
from __future__ import bdfl File "<stdin>", line 1 SyntaxError: future feature bdfl is not defined
:)
The bool-ness of ints is still used widely of course...
Yep, definitely. -Barry
Barry Warsaw wrote:
Except possibly for ancient modules, does anybody still actually use the int-iness of bools explicitly?
I'm not exactly sure what you mean with "int-iness", but C extensions typically do care about the type and use PyInt_* APIs for checking and processing them. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 28 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
Barry Warsaw schrieb am Do, 28. Jul 2011, um 13:27:47 -0400:
Except possibly for ancient modules, does anybody still actually use the int-iness of bools explicitly?
There definitely are applications of using bools as ints. Alex Martelli strongly supports this point of view in this post: http://stackoverflow.com/questions/3174392/is-it-pythonic-to-use-bools-as-in... -- Sven
Wikipedia has a nice survey and comparison of enums in other languages: http://en.wikipedia.org/wiki/Enumerated_type Raymond
I agree with Steven, At first the thought of a standardized enum sounded great. Enums are named constants, this is something Python can already give us. Sometimes the enum values are such that they can be treated as flags. This is a property of integers. In most languages enums are integer constants, because those languages are so much closer to the machine. However Python is a runtime, and it's not Pythonic to attempt to enforce values to be constant, let require that they be integers. Furthermore, it's dangerous to suggest that this be commonplace by adding it to the standard library, when superior Pythonic alternatives exist. If you remove the constraint that enums necessarily correspond to integers, suddenly there is no point: enums for the sake of compatibility and debugging C interfaces, where not even C has that feature built in. While out of tradition, enum like values are still done with integers even in pure Python programs, it seems to me the proper approach is something like: Colors = {'red', 'blue', 'green'} color = 'red' OpenFlags = {'append', 'binary', 'exclusive', 'read', } py_open('/some/path', {'binary', 'exclusive', 'read', 'write'}) Adding enums to the standard library is going to cause a rush to rewrite everything to use enums, where perfectly good alternatives are already in use. I think it should just be accepted that C interfaces work with integers and move on. On Fri, Jul 29, 2011 at 7:16 AM, Raymond Hettinger <raymond.hettinger@gmail.com> wrote:
Wikipedia has a nice survey and comparison of enums in other languages: http://en.wikipedia.org/wiki/Enumerated_type
Raymond _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On Wed, 2011-07-27 at 19:21 -0700, Guido van Rossum wrote:
It's just possible that there's no way to define enums that neither introduced a new (class) scope nor requires a lot of redundant typing.
I wish I could write
red = Enum(1)
and it made the following true:
assert red == 1 assert isinstance(red, int) # a subclass assert str(red) == 'red'
But we'd first need a non-hacky way for Enum() to know that it is being assigned to something named 'red'.
I think the least-hacky way is to live with the redundant typing. When these objects are passed around, (to/from functions or methods), they will most likely be rebound to more generic names that don't match their string repr anyway. The label is not directly tied to the name the object is bound to, although it may match at creation time. What name would you give... colors = [] colors.append(Enum(1)) assert str(colors[0]) == ? The only other nice alternatives I can think of at the moment requires either special syntax or a new keyword. red := 1 # same as... red = Enum('red', 1) enum red, 1 # same as... red = Enum('red', 1) I'd still opt for the more explicit Enum('red', 1) expression first before adding either the syntax or keyword version. Those can come later if it turns out enums are hugely popular. The idea of a new class scope for this hurts my head. To many what if's to consider. Cheers, Ron
ron3200 wrote:
When these objects are passed around, (to/from functions or methods), they will most likely be rebound to more generic names that don't match their string repr anyway.
That doesn't matter, any more than it does for functions and classes. The point is to associate *some* descriptive name with the value for display purposes.
What name would you give...
colors = [] colors.append(Enum(1)) assert str(colors[0]) == ?
Obviously you can't infer any descriptive name from that. Automatic name assignment requires a dedicated syntax, IMO. We just need to wait for the BDFL to find one that he doesn't hate. (Or to stop hating one that he currently hates -- it's happened before...) -- Greg
On 7/27/2011 10:21 PM, Guido van Rossum wrote:
On Wed, Jul 27, 2011 at 6:12 PM, Nick Coghlan<ncoghlan@gmail.com> wrote:
Yeah, I'd actually be inclined to define such values programmatically rather than writing them out manually like that:
_named_colours = dict( red=0xFF0000, green=0x00FF00, blue=0x0000FF, ) globals().update((k, namedvalue(k, v)) for k, v in _named_colours)
Eek, no! It will take the average reader way too long to figure out that that does.
Eek 2. That could be buried in a global_constants function global_constants(red=1,green=2,blue=3)
Static analyzers (which are getting more important) are likely to be fooled by it too. Please remember EIBTI.
But this would be even worse for analyzers. -- Terry Jan Reedy
On Jul 27, 2011, at 07:21 PM, Guido van Rossum wrote:
It's just possible that there's no way to define enums that neither introduced a new (class) scope nor requires a lot of redundant typing.
Ezio had a very nice suggestion, which I've implemented in my experimental branch, e.g.: >>> foo = sys.modules['foo'] >>> Colors.inject(foo) .inject() takes anything that implements the setattr() protocol. So that you could then do: >>> import foo >>> foo.red Colors.red In my experimental branch I've also made EnumValues subclasses of ints. This breaks one test in my Mailman 3 test suite, since I was using a custom encoder for json.dumps() which knew how to encode EnumValues. However, the json module doesn't allow you to override the encoding of basic types, so whereas before my extended encoder's .default() method got called for all the EnumValues being encoded in a REST response, they are now encoded *incorrectly* as their integer values by default. I'd need to figure out a workaround for that, but it does show that EnumValue-as-int is a backward incompatible change. A few other things I've done in my experimental branch: - Added an optional argument `iterable` to make_enum() so that you can use the convenience function to auto-assign integer values other than sequentially incrementing from 1. E.g. >>> def enumiter(): ... start = 1 ... while True: ... yield start ... start <<= 1 >>> make_enum('Flags', 'a b c d e f g', enumiter()) <Flags {a: 1, b: 2, c: 4, d: 8, e: 16, f: 32, g: 64}> - Renamed attributes .enumclass -> .enum and .enumname -> .name (hey, if you're going to be backward incompatible...) If you want to play with it: $ bzr branch lp:~barry/flufl.enum/asint or take a look at: http://bazaar.launchpad.net/~barry/flufl.enum/asint/files The new using.txt file is at: http://tinyurl.com/3s85oq7 Let me know what you think, while I figure out a workaround for the incompatible change. Cheers, -Barry
On Thu, Jul 28, 2011 at 10:12 AM, Barry Warsaw <barry@python.org> wrote:
On Jul 27, 2011, at 07:21 PM, Guido van Rossum wrote:
It's just possible that there's no way to define enums that neither introduced a new (class) scope nor requires a lot of redundant typing.
Ezio had a very nice suggestion, which I've implemented in my experimental branch, e.g.:
>>> foo = sys.modules['foo'] >>> Colors.inject(foo)
.inject() takes anything that implements the setattr() protocol. So that you could then do:
>>> import foo >>> foo.red Colors.red
Nice, but it still hides the definitions from static analyzers. It seems I am almost unique in my insistence that Python's dynamic features be used very sparingly. :-)
In my experimental branch I've also made EnumValues subclasses of ints. This breaks one test in my Mailman 3 test suite, since I was using a custom encoder for json.dumps() which knew how to encode EnumValues. However, the json module doesn't allow you to override the encoding of basic types, so whereas before my extended encoder's .default() method got called for all the EnumValues being encoded in a REST response, they are now encoded *incorrectly* as their integer values by default. I'd need to figure out a workaround for that, but it does show that EnumValue-as-int is a backward incompatible change.
Sure. Almost any change is backward incompatible...
A few other things I've done in my experimental branch:
- Added an optional argument `iterable` to make_enum() so that you can use the convenience function to auto-assign integer values other than sequentially incrementing from 1. E.g.
>>> def enumiter(): ... start = 1 ... while True: ... yield start ... start <<= 1 >>> make_enum('Flags', 'a b c d e f g', enumiter()) <Flags {a: 1, b: 2, c: 4, d: 8, e: 16, f: 32, g: 64}>
- Renamed attributes .enumclass -> .enum and .enumname -> .name (hey, if you're going to be backward incompatible...)
If you want to play with it:
$ bzr branch lp:~barry/flufl.enum/asint
or take a look at:
http://bazaar.launchpad.net/~barry/flufl.enum/asint/files
The new using.txt file is at:
Let me know what you think, while I figure out a workaround for the incompatible change.
Cheers, -Barry
-- --Guido van Rossum (python.org/~guido)
On Thu, Jul 28, 2011 at 10:41 AM, Guido van Rossum <guido@python.org> wrote:
Nice, but it still hides the definitions from static analyzers.
It seems I am almost unique in my insistence that Python's dynamic features be used very sparingly. :-)
Having worked recently on a project that made extensive use of dynamics, I am on your side. In that project, I found it completely impossible to trace a path between any two points in the program short of stepping through it with a debugger. There are classes and functions that are never explicitly referenced anywhere that are found via reflection and then instantiated and called and it is a nightmare. On the other hand, I love dynamic features for writing tests. On Thu, Jul 28, 2011 at 10:12 AM, Barry Warsaw <barry@python.org> wrote:
Ezio had a very nice suggestion, which I've implemented in my experimental branch, e.g.:
foo = sys.modules['foo'] Colors.inject(foo)
.inject() takes anything that implements the setattr() protocol. So that you could then do:
import foo foo.red Colors.red
So in this example, how would I figure out where foo.red is set? I can't do it by looking at the source unless I know about Colors.inject. And what happens if my Weather class has a value Snow and then someone adds that to the Colors enum and clobbers Weather.Snow. I didn't change my class but suddenly I can't make it Snow. --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com
Barry Warsaw wrote:
In my experimental branch I've also made EnumValues subclasses of ints. This breaks one test in my Mailman 3 test suite, since I was using a custom encoder for json.dumps() which knew how to encode EnumValues. However, the json module doesn't allow you to override the encoding of basic types,
Isn't this a problem with json? An EnumValue is a subclass, after all, not a plain ol' straight-up int -- I would think that json should realize a subclass may be different and go the .default() route. ~Ethan~
On Jul 28, 2011, at 10:57 AM, Ethan Furman wrote:
Barry Warsaw wrote:
In my experimental branch I've also made EnumValues subclasses of ints. This breaks one test in my Mailman 3 test suite, since I was using a custom encoder for json.dumps() which knew how to encode EnumValues. However, the json module doesn't allow you to override the encoding of basic types,
Isn't this a problem with json? An EnumValue is a subclass, after all, not a plain ol' straight-up int -- I would think that json should realize a subclass may be different and go the .default() route.
On first blush, I'd agree. _iterencode() just does an isinstance check, which is why this gets caught in the trap. -Barry
On Thu, Jul 28, 2011 at 10:12 AM, Barry Warsaw <barry@python.org> wrote: <snip>
A few other things I've done in my experimental branch:
- Added an optional argument `iterable` to make_enum() so that you can use the convenience function to auto-assign integer values other than sequentially incrementing from 1. E.g.
>>> def enumiter(): ... start = 1 ... while True: ... yield start ... start <<= 1 >>> make_enum('Flags', 'a b c d e f g', enumiter()) <Flags {a: 1, b: 2, c: 4, d: 8, e: 16, f: 32, g: 64}>
Very neat, albeit of questionable enum-ness.
- Renamed attributes .enumclass -> .enum and .enumname -> .name (hey, if you're going to be backward incompatible...)
Excellent. That just leaves SomeEnum(...) vs.SomeEnum[...]. Cheers, Chris
Barry Warsaw wrote:
On Jul 27, 2011, at 07:21 PM, Guido van Rossum wrote:
It's just possible that there's no way to define enums that neither introduced a new (class) scope nor requires a lot of redundant typing.
Ezio had a very nice suggestion, which I've implemented in my experimental branch, e.g.:
>>> foo = sys.modules['foo'] >>> Colors.inject(foo)
.inject() takes anything that implements the setattr() protocol. So that you could then do:
>>> import foo >>> foo.red Colors.red
I've been reading this thread with a growing sense of dismay. I started off excited by the idea that enums would be added to the language, and thinking that Raymond's opposition was uncalled for. Then, as more and more features and metafeatures have been proposed, I've now changed my mind completely and am now with Raymond. I don't even know what these "enums" actually are any more. Part integer, part string, part namespace, part named constant (only not actually constant)... It seems to me that people are trying to dump a whole lot of only vaguely related functionality into a single concept. Including now a proposal to encourage monkey-patching modules by making it an official enum method. For something that is conceptually so simple, this is becoming awfully complicated. -- Steven
On Fri, Jul 29, 2011 at 6:13 AM, Steven D'Aprano <steve@pearwood.info> wrote:
I don't even know what these "enums" actually are any more. Part integer, part string, part namespace, part named constant (only not actually constant)... It seems to me that people are trying to dump a whole lot of only vaguely related functionality into a single concept. Including now a proposal to encourage monkey-patching modules by making it an official enum method.
For something that is conceptually so simple, this is becoming awfully complicated.
Yeah, until this thread reminded me, I'd actually forgotten my own reasons for objecting to standardising enums in the past and proposing named values instead. I think it mostly has to do with degrees of freedom in the design. With named values, there are only a few elements that require explicit design decisions: - ints only, or arbitrary types? (my recipe is designed to work with arbitrary types) - effect on repr() (my recipe modifies it to include a name without breaking any existing repr/eval round trip support) - effect on ascii() (inherits the repr() change, since ascii() is just repr() with a post-processing step) - effect on str() (my recipe makes sure to leave it alone to maximise compatibility) - effect on serialisation (my recipe makes sure to leave it alone to maximise compatibility) - effect on everything else (my recipe makes sure to leave it alone to maximise compatibility) If any application or library wants to name values retrieved from elsewhere (including previously pickled or serialised named values) then it is up to them to apply the transformation at the input points (e.g. a HTTP support library might create a mapping from server response codes to named values, but those values would still be serialised as ordinary integers). It's a (relatively) simple, flexible technique to improve introspection without being overly prescriptive about how to use it. Most design decisions are handled by the basic rule that named values *are* functionally the same as the bare value, they just happen to have been given a specific name that is available for introspection (and is included in the default repr output). Once you take the step up to the enum level and start dictating the *grouping* behaviour along with the naming behaviour, then a whole host of other design issues arise (in addition to the ones that arise for general purpose named values): - behaviour of identity comparisons - behaviour of equivalence comparisons - behaviour of ordering comparisons - behaviour of isinstance checks - API for explicit conversions to and from the underlying type - whether or not to support implicit conversions to and from the underlying type - how to introspect the group contents By giving up the "this is just an ordinary value with a name" design guideline, enums get into a lot of highly arguable territory. Guido *could* cut through that by BDFL fiat if he wanted to, but I'm not sure he *should* - depending on the use case, the preferred answers to some of the design decisions mentioned above are going to legitimately vary. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 28 July 2011 18:12, Barry Warsaw <barry@python.org> wrote:
On Jul 27, 2011, at 07:21 PM, Guido van Rossum wrote:
It's just possible that there's no way to define enums that neither introduced a new (class) scope nor requires a lot of redundant typing.
Ezio had a very nice suggestion, which I've implemented in my experimental branch, e.g.:
foo = sys.modules['foo'] Colors.inject(foo)
Why not Colors.inject(__name__)? This is one line less, the same everywhere, and covers the common case. You could dispatch on string / object if you want to support both. Michael
.inject() takes anything that implements the setattr() protocol. So that you could then do:
import foo foo.red Colors.red
In my experimental branch I've also made EnumValues subclasses of ints. This breaks one test in my Mailman 3 test suite, since I was using a custom encoder for json.dumps() which knew how to encode EnumValues. However, the json module doesn't allow you to override the encoding of basic types, so whereas before my extended encoder's .default() method got called for all the EnumValues being encoded in a REST response, they are now encoded *incorrectly* as their integer values by default. I'd need to figure out a workaround for that, but it does show that EnumValue-as-int is a backward incompatible change.
A few other things I've done in my experimental branch:
- Added an optional argument `iterable` to make_enum() so that you can use the convenience function to auto-assign integer values other than sequentially incrementing from 1. E.g.
def enumiter(): ... start = 1 ... while True: ... yield start ... start <<= 1 make_enum('Flags', 'a b c d e f g', enumiter()) <Flags {a: 1, b: 2, c: 4, d: 8, e: 16, f: 32, g: 64}>
- Renamed attributes .enumclass -> .enum and .enumname -> .name (hey, if you're going to be backward incompatible...)
If you want to play with it:
$ bzr branch lp:~barry/flufl.enum/asint
or take a look at:
http://bazaar.launchpad.net/~barry/flufl.enum/asint/files
The new using.txt file is at:
Let me know what you think, while I figure out a workaround for the incompatible change.
Cheers, -Barry
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On Jul 28, 2011, at 4:42 PM, Michael Foord wrote:
Why not Colors.inject(__name__)?
Because enums aren't import enough to warrant introducing little atrocities into the language. Raymond P.S. I used the term "language" instead of stdlib because I expect enums to be like decorators and context managers in that they will be used almost everywhere -- you won't be able to ignore them. Effectively, they will become a core language feature.
On 29 July 2011 00:58, Raymond Hettinger <raymond.hettinger@gmail.com>wrote:
On Jul 28, 2011, at 4:42 PM, Michael Foord wrote:
Why not Colors.inject(__name__)?
Because enums aren't import enough to warrant introducing little atrocities into the language.
Uh ?? It's the same code just taking a module name instead of requiring you to pull the module out of sys.modules yourself. It's not an atrocity at all. Michael
Raymond
P.S. I used the term "language" instead of stdlib because I expect enums to be like decorators and context managers in that they will be used almost everywhere -- you won't be able to ignore them. Effectively, they will become a core language feature.
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On 29 July 2011 01:03, Michael Foord <fuzzyman@gmail.com> wrote:
On 29 July 2011 00:58, Raymond Hettinger <raymond.hettinger@gmail.com>wrote:
On Jul 28, 2011, at 4:42 PM, Michael Foord wrote:
Why not Colors.inject(__name__)?
Because enums aren't import enough to warrant introducing little atrocities into the language.
Uh ?? It's the same code just taking a module name instead of requiring you to pull the module out of sys.modules yourself. It's not an atrocity at all.
To make it clear, the one-line-works-everywhere alternative people will use is: Colors.inject(sys.modules[__name__]) Which is clearly worse. Michael
Michael
Raymond
P.S. I used the term "language" instead of stdlib because I expect enums to be like decorators and context managers in that they will be used almost everywhere -- you won't be able to ignore them. Effectively, they will become a core language feature.
--
May you do good and not evil
May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On Fri, Jul 29, 2011 at 9:58 AM, Raymond Hettinger <raymond.hettinger@gmail.com> wrote:
P.S. I used the term "language" instead of stdlib because I expect enums to be like decorators and context managers in that they will be used almost everywhere -- you won't be able to ignore them. Effectively, they will become a core language feature.
I agree with this - and I think it's the named value aspect that will make them so pervasive. It's why I keep hitting on collections.namedtuple as a source of inspiration - it adds as little as possible to ordinary tuples to provide the additional benefits. Enums, on the other hand, come with a lot of excess baggage that many potential use cases simply don't need. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 29 July 2011 01:27, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Fri, Jul 29, 2011 at 9:58 AM, Raymond Hettinger <raymond.hettinger@gmail.com> wrote:
P.S. I used the term "language" instead of stdlib because I expect enums to be like decorators and context managers in that they will be used almost everywhere -- you won't be able to ignore them. Effectively, they will become a core language feature.
I agree with this - and I think it's the named value aspect that will make them so pervasive. It's why I keep hitting on collections.namedtuple as a source of inspiration - it adds as little as possible to ordinary tuples to provide the additional benefits. Enums, on the other hand, come with a lot of excess baggage that many potential use cases simply don't need.
I agree "named values" would get us *most* of the value, and would both be less contentious and provide a common building block for third party libraries to build interesting and perhaps esoteric uses on. I *like* grouped named values (I think they make nice apis to read and use when used appropriately), but hey-ho. Michael
Cheers, Nick.
-- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On Thu, Jul 28, 2011 at 5:46 PM, Michael Foord <fuzzyman@gmail.com> wrote:
I agree "named values" would get us *most* of the value, and would both be less contentious and provide a common building block for third party libraries to build interesting and perhaps esoteric uses on.
I *like* grouped named values (I think they make nice apis to read and use when used appropriately), but hey-ho.
These could be two separate features. Named values could be a building block for a built-in Enum type -- the named value would take care of the str() and repr(), while the Enum type would only have to take care of the nice syntax ("class Color(Enum): red = 1; ...") and just create a bunch of named values in the class scope. The metaclass would do something like "for key in classdict: classdict[key] = namedvalue(key, classdict[key])" (though it would probably have to skip certain keys). -- --Guido van Rossum (python.org/~guido)
On Thu, Jul 28, 2011 at 9:11 PM, Guido van Rossum <guido@python.org> wrote:
On Thu, Jul 28, 2011 at 5:46 PM, Michael Foord <fuzzyman@gmail.com> wrote:
I agree "named values" would get us *most* of the value, and would both be less contentious and provide a common building block for third party libraries to build interesting and perhaps esoteric uses on.
I *like* grouped named values (I think they make nice apis to read and use when used appropriately), but hey-ho.
These could be two separate features. Named values could be a building block for a built-in Enum type -- the named value would take care of the str() and repr(), while the Enum type would only have to take care of the nice syntax ("class Color(Enum): red = 1; ...") and just create a bunch of named values in the class scope. The metaclass would do something like "for key in classdict: classdict[key] = namedvalue(key, classdict[key])" (though it would probably have to skip certain keys).
ISTM a NamedValue could be either a copy of the original value or a wrapper around the original. Examples of the former include Nick's recipe and Barry's flufl.enum asint branch. Examples of the latter include the flufl.enum trunk. The copy version requires making the copy, which is less convenient if the value's type needs more than the instance to make a copy. The wrapper version probably wouldn't work well as a drop-in replacement for the original value. So if a NamedValue were to make it into the stdlib, which would it be? Does NamedValue add much value if not coupled with grouping? -eric p.s. FWIW I think Nick's recipe for NamedValue is the right approach for the copied-value model.
--Guido van Rossum (python.org/~guido) _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On Fri, Jul 29, 2011 at 2:40 PM, Eric Snow <ericsnowcurrently@gmail.com> wrote:
ISTM a NamedValue could be either a copy of the original value or a wrapper around the original. Examples of the former include Nick's recipe and Barry's flufl.enum asint branch. Examples of the latter include the flufl.enum trunk.
The copy version requires making the copy, which is less convenient if the value's type needs more than the instance to make a copy. The wrapper version probably wouldn't work well as a drop-in replacement for the original value.
A weakref.proxy style API can actually be a fairly effective dropin replacement for a type. However, it's a high overhead and quite complicated solution.
So if a NamedValue were to make it into the stdlib, which would it be?
Arguments can be made in both directions. The main benefit of inheritance is that things behave correctly by default, and the only behavioural differences are those we add. Proxying can definitely work, but it's actually hard to do such that special method invocations work properly without giving false positives on hasattr() and ABC isinstance() checks.
Does NamedValue add much value if not coupled with grouping?
Yeah, it provides the oft-cited debugging benefits of having the name of the value appearing in the representation. That's invaluable for cases where you don't actually control the display operation (e.g logging messages in library code). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Guido van Rossum <guido@python.org> writes:
These could be two separate features. Named values could be a building block for a built-in Enum type -- the named value would take care of the str() and repr(), while the Enum type would only have to take care of the nice syntax ("class Color(Enum): red = 1; ...") and just create a bunch of named values in the class scope.
+1. As for EnumValue instances having a defined order, thanks to my reading of <URL:http://en.wikipedia.org/wiki/Enumerated_type> I can now say that some of us want an enumerated type that is not ordinal, and others want an ordinal type. And non-ordinal enumerated types, built-in, could then further be used as the basis for a custom ordinal type. Hence: * a built-in “named value” feature * a built-in, or standard-library, non-ordinal enumerated type feature (using the “named value” feature) * a standard-library ordinal type feature (using the enumerated type feature) Would that make us all happy? -- \ “We are stuck with technology when what we really want is just | `\ stuff that works.” —Douglas Adams | _o__) | Ben Finney
On Fri, Jul 29, 2011 at 3:00 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
Guido van Rossum <guido@python.org> writes:
These could be two separate features. Named values could be a building block for a built-in Enum type -- the named value would take care of the str() and repr(), while the Enum type would only have to take care of the nice syntax ("class Color(Enum): red = 1; ...") and just create a bunch of named values in the class scope.
+1.
As for EnumValue instances having a defined order, thanks to my reading of <URL:http://en.wikipedia.org/wiki/Enumerated_type> I can now say that some of us want an enumerated type that is not ordinal, and others want an ordinal type.
And non-ordinal enumerated types, built-in, could then further be used as the basis for a custom ordinal type.
Hence:
* a built-in “named value” feature
* a built-in, or standard-library, non-ordinal enumerated type feature (using the “named value” feature)
* a standard-library ordinal type feature (using the enumerated type feature)
Would that make us all happy?
I like the tower, but I'd like to see us approach this incrementally. 1. Design and provide a standard "named value" feature (CPython unfortunately makes it difficult to do this efficiently - we need to add state to hold the name, but doing that in a way that is compatible with the C layout of tuples and similar objects means the state has to be stored in __dict__) 2. Perhaps provide a non-ordinal NamedGroup feature (avoiding the Enum naming with its 'enumerated' ordinal implications) Explicitly naming several values is likely to be clumsy enough that a metaclass based solution may be a beneficial convenience API, but Named Groups wouldn't enforce any additional behaviour on their members. I'm not completely persuaded on this point as yet, but I can see the merit of the argument. As an initial step, I thinks that's the absolute limit of how far we should go. I see it as similar to the handling of function annotations - we can provide a standard infrastructure for naming instances without otherwise affecting their behaviour, and then allow third parties to build on that and see where they take it. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Fri, Jul 29, 2011 at 6:11 AM, Guido van Rossum <guido@python.org> wrote:
On Thu, Jul 28, 2011 at 5:46 PM, Michael Foord <fuzzyman@gmail.com> wrote:
I agree "named values" would get us *most* of the value, and would both be less contentious and provide a common building block for third party libraries to build interesting and perhaps esoteric uses on.
I *like* grouped named values (I think they make nice apis to read and use when used appropriately), but hey-ho.
These could be two separate features. Named values could be a building block for a built-in Enum type -- the named value would take care of the str() and repr(), while the Enum type would only have to take care of the nice syntax ("class Color(Enum): red = 1; ...") and just create a bunch of named values in the class scope. The metaclass would do something like "for key in classdict: classdict[key] = namedvalue(key, classdict[key])" (though it would probably have to skip certain keys).
The problem with named value is that: namedvalue(1, "red") Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself). So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check. -- Paul
Paul Colomiets wrote:
On Fri, Jul 29, 2011 at 6:11 AM, Guido van Rossum <guido@python.org> wrote:
On Thu, Jul 28, 2011 at 5:46 PM, Michael Foord <fuzzyman@gmail.com> wrote:
I agree "named values" would get us *most* of the value, and would both be less contentious and provide a common building block for third party libraries to build interesting and perhaps esoteric uses on.
I *like* grouped named values (I think they make nice apis to read and use when used appropriately), but hey-ho.
These could be two separate features. Named values could be a building block for a built-in Enum type -- the named value would take care of the str() and repr(), while the Enum type would only have to take care of the nice syntax ("class Color(Enum): red = 1; ...") and just create a bunch of named values in the class scope. The metaclass would do something like "for key in classdict: classdict[key] = namedvalue(key, classdict[key])" (though it would probably have to skip certain keys).
The problem with named value is that:
namedvalue(1, "red")
Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself).
So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check.
A meta class variant could easily add class and module names to the attrbute name, if needed/wanted. Just to make sure: namedvalue() will just change the repr() of the value, not the str() of it, right ? I think that's essential for making namedvalue()s useful in practice, since you mostly need this for debugging and error reporting and don't want the namevalue aspect of a constant to get in the way of its normal use in e.g. conversion to text formats such as JSON, XML, etc. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 29 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Fri, Jul 29, 2011 at 7:37 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Paul Colomiets wrote:
The problem with named value is that:
namedvalue(1, "red")
Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself).
So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check.
A meta class variant could easily add class and module names to the attrbute name, if needed/wanted.
Given that the status quo is for "TerminalColors.red" et al to just display as "1" (or whatever integers they're assigned), I'm finding it hard to consider this a particularly significant criticism. If we don't even know what *kind* of values are being passed to a debugging message, we have bigger problems than whether or not those values have been given names (and searching for all named values of 'red' with a value of '1' is going to create a much smaller resulting set than searching for all values of '1' ).
Just to make sure:
namedvalue() will just change the repr() of the value, not the str() of it, right ?
Yeah, I figured out the mechanics needed to make that work properly when writing up the recipe for the cookbook (http://code.activestate.com/recipes/577810-named-values/)
I think that's essential for making namedvalue()s useful in practice, since you mostly need this for debugging and error reporting and don't want the namevalue aspect of a constant to get in the way of its normal use in e.g. conversion to text formats such as JSON, XML, etc.
Yeah, the case I first thought of was writing numbers to CSV files, but it's really any data export operation. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Fri, Jul 29, 2011 at 7:55 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Fri, Jul 29, 2011 at 7:37 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Paul Colomiets wrote:
The problem with named value is that:
namedvalue(1, "red")
Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself).
So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check.
A meta class variant could easily add class and module names to the attrbute name, if needed/wanted.
Given that the status quo is for "TerminalColors.red" et al to just display as "1" (or whatever integers they're assigned), I'm finding it hard to consider this a particularly significant criticism. If we don't even know what *kind* of values are being passed to a debugging message, we have bigger problems than whether or not those values have been given names (and searching for all named values of 'red' with a value of '1' is going to create a much smaller resulting set than searching for all values of '1' ).
Just to make sure:
namedvalue() will just change the repr() of the value, not the str() of it, right ?
Yeah, I figured out the mechanics needed to make that work properly when writing up the recipe for the cookbook (http://code.activestate.com/recipes/577810-named-values/)
I think that's essential for making namedvalue()s useful in practice, since you mostly need this for debugging and error reporting and don't want the namevalue aspect of a constant to get in the way of its normal use in e.g. conversion to text formats such as JSON, XML, etc.
Yeah, the case I first thought of was writing numbers to CSV files, but it's really any data export operation.
That's assuming the data export format doesn't support enums. I would fine enums whose str() is just an int pretty annoying. Note that str(True) == repr(True) == 'True' and that's how I'd like it to be for enums in general. If you want the int value, use int(e). If you want the class name, use e.__class__.__name__ (maybe e.__class__ is what you're after anyway). My observation is that most enums have sufficiently unique names that during a debugging session the class is not a big mystery; it's the mapping from value to name that's hard to memorize. As a compromise, I could live with str(e) == 'red' and repr(e) == 'Color.red'. But I don't think I could stomach str(e) == 1. -- --Guido van Rossum (python.org/~guido)
On Sat, Jul 30, 2011 at 1:09 AM, Guido van Rossum <guido@python.org> wrote:
That's assuming the data export format doesn't support enums.
I would fine enums whose str() is just an int pretty annoying. Note that str(True) == repr(True) == 'True' and that's how I'd like it to be for enums in general. If you want the int value, use int(e). If you want the class name, use e.__class__.__name__ (maybe e.__class__ is what you're after anyway). My observation is that most enums have sufficiently unique names that during a debugging session the class is not a big mystery; it's the mapping from value to name that's hard to memorize. As a compromise, I could live with str(e) == 'red' and repr(e) == 'Color.red'. But I don't think I could stomach str(e) == 1.
For enums that define new values which are not equivalent to the underlying values, I agree with you, but my concept for named values is to allow them to be almost entirely a drop-in replacement for the values they replace. For that more general use case, changing the result of str() would be problematic, since str() is often used to create command line APIs and other things. With a named value 'nv.__name__' would give the name, while 'str(nv)' would give the original unadorned string representation. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Sat, 30 Jul 2011 01:26:01 +1000 Nick Coghlan <ncoghlan@gmail.com> wrote:
On Sat, Jul 30, 2011 at 1:09 AM, Guido van Rossum <guido@python.org> wrote:
That's assuming the data export format doesn't support enums.
I would fine enums whose str() is just an int pretty annoying. Note that str(True) == repr(True) == 'True' and that's how I'd like it to be for enums in general. If you want the int value, use int(e). If you want the class name, use e.__class__.__name__ (maybe e.__class__ is what you're after anyway). My observation is that most enums have sufficiently unique names that during a debugging session the class is not a big mystery; it's the mapping from value to name that's hard to memorize. As a compromise, I could live with str(e) == 'red' and repr(e) == 'Color.red'. But I don't think I could stomach str(e) == 1.
For enums that define new values which are not equivalent to the underlying values, I agree with you, but my concept for named values is to allow them to be almost entirely a drop-in replacement for the values they replace. For that more general use case, changing the result of str() would be problematic, since str() is often used to create command line APIs and other things.
I agree with that. repr() can be used to give further information (and repr() or even ascii() should always be used when debugging anyway, not str()). Realistically, many potential uses of enums are tied to some underlying C library - be it errnos, flags for os.open(), socket families, etc. It would probably be wrong for an enum implementation not to support these use cases. Regards Antoine.
Guido van Rossum wrote:
On Fri, Jul 29, 2011 at 7:55 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Fri, Jul 29, 2011 at 7:37 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Paul Colomiets wrote:
The problem with named value is that:
namedvalue(1, "red")
Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself).
So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check.
A meta class variant could easily add class and module names to the attrbute name, if needed/wanted.
Given that the status quo is for "TerminalColors.red" et al to just display as "1" (or whatever integers they're assigned), I'm finding it hard to consider this a particularly significant criticism. If we don't even know what *kind* of values are being passed to a debugging message, we have bigger problems than whether or not those values have been given names (and searching for all named values of 'red' with a value of '1' is going to create a much smaller resulting set than searching for all values of '1' ).
Just to make sure:
namedvalue() will just change the repr() of the value, not the str() of it, right ?
Yeah, I figured out the mechanics needed to make that work properly when writing up the recipe for the cookbook (http://code.activestate.com/recipes/577810-named-values/)
I think that's essential for making namedvalue()s useful in practice, since you mostly need this for debugging and error reporting and don't want the namevalue aspect of a constant to get in the way of its normal use in e.g. conversion to text formats such as JSON, XML, etc.
Yeah, the case I first thought of was writing numbers to CSV files, but it's really any data export operation.
That's assuming the data export format doesn't support enums.
I would fine enums whose str() is just an int pretty annoying. Note that str(True) == repr(True) == 'True' and that's how I'd like it to be for enums in general. If you want the int value, use int(e). If you want the class name, use e.__class__.__name__ (maybe e.__class__ is what you're after anyway). My observation is that most enums have sufficiently unique names that during a debugging session the class is not a big mystery; it's the mapping from value to name that's hard to memorize. As a compromise, I could live with str(e) == 'red' and repr(e) == 'Color.red'. But I don't think I could stomach str(e) == 1.
The situation with enums is different than for booleans: many data interchange formats out there support true/false directly, so there's little to change and little breakage involved. And it's easy to fix, since you only to deal with two such enums. However, when you change existing code constants to enums, chances are high that your interchange libraries are going to fail because of this, or your application will start providing broken data to external resources, without you knowing. Since it's easy to switch from %s to %r, if needed, I don't see much of a problem with having str(e) continue to return the integer string. Regarding the repr(e): This should really be user-defined. There situations where you want to see both the integer code and description in the repr(e), e.g. when dealing with error codes where you quickly need to communicate the error, so a format like '[123] Error contacting server' would be better than just 'Error contacting server'. In other cases, the subsystem is important, so getting '[Server][FTP] Permission error' is more useful than just 'Permission error'. And in yet other circumstances, you want to see the defining module and class. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 29 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
Well, it does sound like we're not going to agree on how enums should behave. There are too many different use cases. So I am giving up. Sorry. (Also I'm going on vacation and will be mostly off-line for the next 2-3 weeks.) --Guido On Fri, Jul 29, 2011 at 10:07 AM, M.-A. Lemburg <mal@egenix.com> wrote:
Guido van Rossum wrote:
On Fri, Jul 29, 2011 at 7:55 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Fri, Jul 29, 2011 at 7:37 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Paul Colomiets wrote:
The problem with named value is that:
namedvalue(1, "red")
Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself).
So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check.
A meta class variant could easily add class and module names to the attrbute name, if needed/wanted.
Given that the status quo is for "TerminalColors.red" et al to just display as "1" (or whatever integers they're assigned), I'm finding it hard to consider this a particularly significant criticism. If we don't even know what *kind* of values are being passed to a debugging message, we have bigger problems than whether or not those values have been given names (and searching for all named values of 'red' with a value of '1' is going to create a much smaller resulting set than searching for all values of '1' ).
Just to make sure:
namedvalue() will just change the repr() of the value, not the str() of it, right ?
Yeah, I figured out the mechanics needed to make that work properly when writing up the recipe for the cookbook (http://code.activestate.com/recipes/577810-named-values/)
I think that's essential for making namedvalue()s useful in practice, since you mostly need this for debugging and error reporting and don't want the namevalue aspect of a constant to get in the way of its normal use in e.g. conversion to text formats such as JSON, XML, etc.
Yeah, the case I first thought of was writing numbers to CSV files, but it's really any data export operation.
That's assuming the data export format doesn't support enums.
I would fine enums whose str() is just an int pretty annoying. Note that str(True) == repr(True) == 'True' and that's how I'd like it to be for enums in general. If you want the int value, use int(e). If you want the class name, use e.__class__.__name__ (maybe e.__class__ is what you're after anyway). My observation is that most enums have sufficiently unique names that during a debugging session the class is not a big mystery; it's the mapping from value to name that's hard to memorize. As a compromise, I could live with str(e) == 'red' and repr(e) == 'Color.red'. But I don't think I could stomach str(e) == 1.
The situation with enums is different than for booleans: many data interchange formats out there support true/false directly, so there's little to change and little breakage involved. And it's easy to fix, since you only to deal with two such enums.
However, when you change existing code constants to enums, chances are high that your interchange libraries are going to fail because of this, or your application will start providing broken data to external resources, without you knowing.
Since it's easy to switch from %s to %r, if needed, I don't see much of a problem with having str(e) continue to return the integer string.
Regarding the repr(e): This should really be user-defined. There situations where you want to see both the integer code and description in the repr(e), e.g. when dealing with error codes where you quickly need to communicate the error, so a format like '[123] Error contacting server' would be better than just 'Error contacting server'.
In other cases, the subsystem is important, so getting '[Server][FTP] Permission error' is more useful than just 'Permission error'.
And in yet other circumstances, you want to see the defining module and class.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Jul 29 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! ::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
-- --Guido van Rossum (python.org/~guido)
Guido van Rossum wrote:
Well, it does sound like we're not going to agree on how enums should behave. There are too many different use cases. So I am giving up. Sorry. (Also I'm going on vacation and will be mostly off-line for the next 2-3 weeks.)
Perhaps we should try to address those use cases one by one, instead of trying to lump everything into one new object type. OTOH, I also do think we should be conservative about adding such new features to the stdlib. There are enough existing implementations on PyPI and the cookbook (including the one that implements the rejected PEP 354) to choose from and there is no one-fits-all type of named-int-that-belongs-to-a-value-set. Have a nice vacation, -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 29 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
M.-A. Lemburg wrote:
Guido van Rossum wrote:
On Fri, Jul 29, 2011 at 7:55 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Fri, Jul 29, 2011 at 7:37 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Paul Colomiets wrote:
The problem with named value is that:
namedvalue(1, "red")
Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself).
So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check.
A meta class variant could easily add class and module names to the attrbute name, if needed/wanted.
Given that the status quo is for "TerminalColors.red" et al to just display as "1" (or whatever integers they're assigned), I'm finding it hard to consider this a particularly significant criticism. If we don't even know what *kind* of values are being passed to a debugging message, we have bigger problems than whether or not those values have been given names (and searching for all named values of 'red' with a value of '1' is going to create a much smaller resulting set than searching for all values of '1' ).
Just to make sure:
namedvalue() will just change the repr() of the value, not the str() of it, right ?
Yeah, I figured out the mechanics needed to make that work properly when writing up the recipe for the cookbook (http://code.activestate.com/recipes/577810-named-values/)
I think that's essential for making namedvalue()s useful in practice, since you mostly need this for debugging and error reporting and don't want the namevalue aspect of a constant to get in the way of its normal use in e.g. conversion to text formats such as JSON, XML, etc.
Yeah, the case I first thought of was writing numbers to CSV files, but it's really any data export operation.
That's assuming the data export format doesn't support enums.
I would fine enums whose str() is just an int pretty annoying. Note that str(True) == repr(True) == 'True' and that's how I'd like it to be for enums in general. If you want the int value, use int(e). If you want the class name, use e.__class__.__name__ (maybe e.__class__ is what you're after anyway). My observation is that most enums have sufficiently unique names that during a debugging session the class is not a big mystery; it's the mapping from value to name that's hard to memorize. As a compromise, I could live with str(e) == 'red' and repr(e) == 'Color.red'. But I don't think I could stomach str(e) == 1.
The situation with enums is different than for booleans: many data interchange formats out there support true/false directly, so there's little to change and little breakage involved. And it's easy to fix, since you only to deal with two such enums.
However, when you change existing code constants to enums, chances are high that your interchange libraries are going to fail because of this, or your application will start providing broken data to external resources, without you knowing.
Since it's easy to switch from %s to %r, if needed, I don't see much of a problem with having str(e) continue to return the integer string.
Regarding the repr(e): This should really be user-defined. There situations where you want to see both the integer code and description in the repr(e), e.g. when dealing with error codes where you quickly need to communicate the error, so a format like '[123] Error contacting server' would be better than just 'Error contacting server'.
In other cases, the subsystem is important, so getting '[Server][FTP] Permission error' is more useful than just 'Permission error'.
And in yet other circumstances, you want to see the defining module and class.
Thinking about this some more: it would be good to have attributes .name - namedvalue name .value - namedvalue value object on enums and have str(e) == str(e.value) and repr(e) == e.name. BTW: Would it be possible to have work for more than just integers ? If not, "namedint" may be a more intuitive name. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 29 2011)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Fri, Jul 29, 2011 at 10:40 AM, M.-A. Lemburg <mal@egenix.com> wrote:
M.-A. Lemburg wrote:
Guido van Rossum wrote:
On Fri, Jul 29, 2011 at 7:55 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Fri, Jul 29, 2011 at 7:37 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Paul Colomiets wrote:
The problem with named value is that:
namedvalue(1, "red")
Will give you are repr of "red". And you don't know whether it's TerminalColors.red or WebSafe.red or BuildbotState.red. And most of us will be too lazy to add group name or module name directly into the named value (partially because it's repeating yourself).
So the big feature of flufl.enum is classname (call it group name) in repr and isinstance check.
A meta class variant could easily add class and module names to the attrbute name, if needed/wanted.
Given that the status quo is for "TerminalColors.red" et al to just display as "1" (or whatever integers they're assigned), I'm finding it hard to consider this a particularly significant criticism. If we don't even know what *kind* of values are being passed to a debugging message, we have bigger problems than whether or not those values have been given names (and searching for all named values of 'red' with a value of '1' is going to create a much smaller resulting set than searching for all values of '1' ).
Just to make sure:
namedvalue() will just change the repr() of the value, not the str() of it, right ?
Yeah, I figured out the mechanics needed to make that work properly when writing up the recipe for the cookbook (http://code.activestate.com/recipes/577810-named-values/)
I think that's essential for making namedvalue()s useful in practice, since you mostly need this for debugging and error reporting and don't want the namevalue aspect of a constant to get in the way of its normal use in e.g. conversion to text formats such as JSON, XML, etc.
Yeah, the case I first thought of was writing numbers to CSV files, but it's really any data export operation.
That's assuming the data export format doesn't support enums.
I would fine enums whose str() is just an int pretty annoying. Note that str(True) == repr(True) == 'True' and that's how I'd like it to be for enums in general. If you want the int value, use int(e). If you want the class name, use e.__class__.__name__ (maybe e.__class__ is what you're after anyway). My observation is that most enums have sufficiently unique names that during a debugging session the class is not a big mystery; it's the mapping from value to name that's hard to memorize. As a compromise, I could live with str(e) == 'red' and repr(e) == 'Color.red'. But I don't think I could stomach str(e) == 1.
The situation with enums is different than for booleans: many data interchange formats out there support true/false directly, so there's little to change and little breakage involved. And it's easy to fix, since you only to deal with two such enums.
However, when you change existing code constants to enums, chances are high that your interchange libraries are going to fail because of this, or your application will start providing broken data to external resources, without you knowing.
Since it's easy to switch from %s to %r, if needed, I don't see much of a problem with having str(e) continue to return the integer string.
Regarding the repr(e): This should really be user-defined. There situations where you want to see both the integer code and description in the repr(e), e.g. when dealing with error codes where you quickly need to communicate the error, so a format like '[123] Error contacting server' would be better than just 'Error contacting server'.
In other cases, the subsystem is important, so getting '[Server][FTP] Permission error' is more useful than just 'Permission error'.
And in yet other circumstances, you want to see the defining module and class.
Thinking about this some more: it would be good to have attributes
.name - namedvalue name .value - namedvalue value object
on enums and have str(e) == str(e.value) and repr(e) == e.name.
BTW: Would it be possible to have work for more than just integers ? If not, "namedint" may be a more intuitive name.
Nick's proposal for named values is to work for any type. But I think that the things called enums ought to be an int subclass. I guess if enums are named values their value attribute should return a plain int with the same value. -- --Guido van Rossum (python.org/~guido)
Hi, On 28/07/2011 4.12, Nick Coghlan wrote:
On Thu, Jul 28, 2011 at 10:56 AM, Barry Warsaw<barry@python.org> wrote:
Again, looking at how I've used them extensively over the last several years, I would much rather write
class Colors(Enum): red = 1 green = 2 blue = 3
than
red = NamedValue('red', 1) green = NamedValue('green', 2) blue = NamedValue('blue', 3)
To me, the duplication is jarring and error prone. Yeah, I'd actually be inclined to define such values programmatically rather than writing them out manually like that:
_named_colours = dict( red=0xFF0000, green=0x00FF00, blue=0x0000FF, ) globals().update((k, namedvalue(k, v)) for k, v in _named_colours)
A method like Colors.make_global([namespace]) could be added to do the same thing.
(where namedvalue is the value based factory function I mentioned in the recipe post)
However, my contention is that the fundamentally interesting operation is associating names with values (as your EnumValue class does). Enums and their ilk are then just syntactic sugar for defining groups of such values without needing to repeat yourself.
Cheers, Nick.
Best Regards, Ezio Melotti
On 28 July 2011 13:17, Ezio Melotti <ezio.melotti@gmail.com> wrote:
Hi,
On 28/07/2011 4.12, Nick Coghlan wrote:
On Thu, Jul 28, 2011 at 10:56 AM, Barry Warsaw<barry@python.org> wrote:
Again, looking at how I've used them extensively over the last several years, I would much rather write
class Colors(Enum): red = 1 green = 2 blue = 3
than
red = NamedValue('red', 1) green = NamedValue('green', 2) blue = NamedValue('blue', 3)
To me, the duplication is jarring and error prone.
Yeah, I'd actually be inclined to define such values programmatically rather than writing them out manually like that:
_named_colours = dict( red=0xFF0000, green=0x00FF00, blue=0x0000FF, ) globals().update((k, namedvalue(k, v)) for k, v in _named_colours)
A method like Colors.make_global([namespace]**) could be added to do the same thing.
Colors.make_global(__name__) Michael
(where namedvalue is the value based factory function I mentioned in the recipe post)
However, my contention is that the fundamentally interesting operation is associating names with values (as your EnumValue class does). Enums and their ilk are then just syntactic sugar for defining groups of such values without needing to repeat yourself.
Cheers, Nick.
Best Regards, Ezio Melotti
______________________________**_________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/**mailman/listinfo/python-ideas<http://mail.python.org/mailman/listinfo/python-ideas>
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On Wed, Jul 27, 2011 at 5:30 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
This recipe is pretty much the full extent of the proposal (perhaps with the automatic type generation I mention in the discussion section): http://code.activestate.com/recipes/577810-named-values/
I like the NamedValue/namedvalue recipe. I prefer module constants like os.SEEK_END to nested names like os.Seek.End and this encourages that. If someone decides to change os module to: SEEK_END = namedvalue('SEEK_END', 2) that won't break anything. For the simple enum case, a decorator could transform @enum class Color: red = 1 green = 2 into: class Color: red = namedvalue('Color.red', 1) green = namedvalue('Color.green', 2) --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com
On Thursday 28.07.2011 00:24:09 Nick Coghlan wrote:
1. I find the "enum" concept too limiting. NamedValue (as a mixin class) is a better building block since it separates the naming aspect from the "group of values" aspect implied by enum. It would be nice, for example, if we could do things like "tau = NamedFloat('tau', 2*math.pi)" where NamedFloat is essentially just "class NamedFloat(NamedValue, float): pass" (this could, of course, be hidden behind a factory function that used type(value) and a cache to dynamically create appropriate subclasses)
+1 Very nice idea!
On 7/25/11 1:43 PM, Michael Foord wrote:
Some apis (for example those exported directly from C) can't work with something that isn't a real int.
I think almost all of those will usually accept an object that implements __int__ and __index__, don't they? E.g. [~] |1> import os [~] |2> class A(object): ..> def __init__(self,x): ..> self.x = x ..> def __int__(self): ..> return self.x ..> def __index__(self): ..> return self.x ..> [~] |10> os.open('foo', A(os.O_RDWR)) 21 os.open() just uses PyArg_ParseTuple(), like most extension functions. I'm sure you could write an extension function that would reject A() instances, but it's more work, so most people don't. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
On 25 July 2011 21:47, Robert Kern <robert.kern@gmail.com> wrote:
On 7/25/11 1:43 PM, Michael Foord wrote:
Some apis (for example those exported directly from C) can't work with
something that isn't a real int.
I think almost all of those will usually accept an object that implements __int__ and __index__, don't they? E.g.
[~] |1> import os
[~] |2> class A(object): ..> def __init__(self,x): ..> self.x = x ..> def __int__(self): ..> return self.x ..> def __index__(self): ..> return self.x ..>
[~] |10> os.open('foo', A(os.O_RDWR)) 21
os.open() just uses PyArg_ParseTuple(), like most extension functions. I'm sure you could write an extension function that would reject A() instances, but it's more work, so most people don't.
Providing __index__ may indeed be enough. Michael
-- Robert Kern
"I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
______________________________**_________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/**mailman/listinfo/python-ideas<http://mail.python.org/mailman/listinfo/python-ideas>
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On 7/25/11 4:39 PM, Michael Foord wrote:
On 25 July 2011 21:47, Robert Kern <robert.kern@gmail.com <mailto:robert.kern@gmail.com>> wrote:
On 7/25/11 1:43 PM, Michael Foord wrote:
Some apis (for example those exported directly from C) can't work with something that isn't a real int.
I think almost all of those will usually accept an object that implements __int__ and __index__, don't they? E.g.
[~] |1> import os
[~] |2> class A(object): ..> def __init__(self,x): ..> self.x = x ..> def __int__(self): ..> return self.x ..> def __index__(self): ..> return self.x ..>
[~] |10> os.open('foo', A(os.O_RDWR)) 21
os.open() just uses PyArg_ParseTuple(), like most extension functions. I'm sure you could write an extension function that would reject A() instances, but it's more work, so most people don't.
Providing __index__ may indeed be enough.
From experiment with os.open() under 2.7 and 3.1, it is not enough, though perhaps either PyArg_ParseTuple() or PyInt_As*() should be modified to make it so. -- Robert Kern "I have come to believe that the whole world is an enigma, a harmless enigma that is made terrible by our own mad attempt to interpret it as though it had an underlying truth." -- Umberto Eco
Barry Warsaw wrote:
On Jul 25, 2011, at 05:46 PM, Michael Foord wrote:
If the flufl enums subclassed integer then replacing most of the existing constants in the standard library would be trivially easy (so we'd have a built-in use case).
I've so far resisted the occasional suggestion to make enum values int subclasses, because I think enum values should not *be* ints, but should be compatible with ints (as they are now). Ordered comparisons of enum values bothers me. ;)
If it's going to be in the stdlib, I think they should be ints -- while not necessary most of the time, the few times it is will illicit praise for Python and its simplicity instead of complaints about somebody making decisions for them. ~Ethan~
On Jul 24, 2011, at 05:33 PM, Guido van Rossum wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
Thanks! I would be quite happy to see the library pulled into the stdlib, and I'd be willing to maintain a backward compatible standalone version for older Pythons. For me, the API has been quite stable. The last thing I added was Michael Foord's make_enum() contribution, and PJE's fix for pickling. I haven't felt a burning need to add anything else really in quite some time. (I'll respond to specific details later in the thread.) I'm sure my contributors agreement would cover this, and I'd be happy to release it under the APLv2.0 for that purpose. -Barry
2011/7/25 Barry Warsaw <barry@python.org>
On Jul 24, 2011, at 05:33 PM, Guido van Rossum wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
Thanks! I would be quite happy to see the library pulled into the stdlib, and I'd be willing to maintain a backward compatible standalone version for older Pythons.
For me, the API has been quite stable. The last thing I added was Michael Foord's make_enum() contribution, and PJE's fix for pickling. I haven't felt a burning need to add anything else really in quite some time.
from flufl.enum import Enum class Colors(Enum): ... red = 1 ... green = 2 ... blue = 3
What I find uncomfortable with this is the extra "Colors" name space. Say we change all the socket module constants to make use of this, what will we have?
socket.AF_INET <EnumValue: Constants.AF_INET [int=2]>
...and:
socket.Constants.AF_INET <EnumValue: Constants.AF_INET [int=2]>
...? Is that correct? Also:
socket.AF_INET <EnumValue: Constants.AF_INET [int=2]> socket.AF_INET == 2 False
This shouldn't be underestimated: I've seen code comparing against plain integers rather than constants more than once, and that code would eventually break. Personally I've never felt the need of any kind of "enum" type. On the other hand I would welcome a "named constant" type of some kind but IMO, it should remain a base integer and provide just a minimal extra layer of usability, such as: class constant(int): """A constant type; overrides base int to provide a useful name on str().""" def __new__(cls, value, name, doc=None): inst = super(constant, cls).__new__(cls, value) inst._name = name if doc is not None: inst.__doc__ = doc return inst def __str__(self): return self._name
STATUS_RUNNING = constant(0, "running") STATUS_RUNNING 0 str(STATUS_RUNNING) "running"
In summary, I'm -1 about including this into the stdlib and change all the stdlib constants in accordarce. My 2 cents. --- Giampaolo http://code.google.com/p/pyftpdlib/<http://code.google.com/p/psutil/downloads/list> http://code.google.com/p/psutil/<http://code.google.com/p/psutil/downloads/list>
On 27 July 2011 12:57, Giampaolo Rodolà <g.rodola@gmail.com> wrote:
2011/7/25 Barry Warsaw <barry@python.org>
On Jul 24, 2011, at 05:33 PM, Guido van Rossum wrote:
For enums, I think we should just pick a solution. I'm in favor of Barry Warsaw's version, flufl.enum.
Thanks! I would be quite happy to see the library pulled into the stdlib, and I'd be willing to maintain a backward compatible standalone version for older Pythons.
For me, the API has been quite stable. The last thing I added was Michael Foord's make_enum() contribution, and PJE's fix for pickling. I haven't felt a burning need to add anything else really in quite some time.
from flufl.enum import Enum class Colors(Enum): ... red = 1 ... green = 2 ... blue = 3
What I find uncomfortable with this is the extra "Colors" name space. Say we change all the socket module constants to make use of this, what will we have?
socket.AF_INET <EnumValue: Constants.AF_INET [int=2]>
...and:
socket.Constants.AF_INET <EnumValue: Constants.AF_INET [int=2]>
...? Is that correct?
It could be. We can do what what we want.
Also:
socket.AF_INET <EnumValue: Constants.AF_INET [int=2]> socket.AF_INET == 2 False
This shouldn't be underestimated: I've seen code comparing against plain integers rather than constants more than once, and that code would eventually break.
Immediately break. I agree that this is a problem - new constants should compare equal to their old values if we use them in the standard library.
Personally I've never felt the need of any kind of "enum" type. On the other hand I would welcome a "named constant" type of some kind but IMO, it should remain a base integer and provide just a minimal extra layer of usability, such as:
Sure. Providing grouping for named constants for simpler defining of them is a nice feature though. We should *not* add enums but instead should have named constants and grouped constants. Michael
class constant(int): """A constant type; overrides base int to provide a useful name on str()."""
def __new__(cls, value, name, doc=None): inst = super(constant, cls).__new__(cls, value) inst._name = name if doc is not None: inst.__doc__ = doc return inst
def __str__(self): return self._name
STATUS_RUNNING = constant(0, "running") STATUS_RUNNING 0 str(STATUS_RUNNING) "running"
In summary, I'm -1 about including this into the stdlib and change all the stdlib constants in accordarce.
My 2 cents.
--- Giampaolo http://code.google.com/p/pyftpdlib/<http://code.google.com/p/psutil/downloads/list> http://code.google.com/p/psutil/<http://code.google.com/p/psutil/downloads/list>
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
participants (29)
-
Antoine Pitrou
-
Barry Warsaw
-
Ben Finney
-
Bruce Leban
-
Chris Rebert
-
Eike Welk
-
Eric Snow
-
Ethan Furman
-
Ezio Melotti
-
Giampaolo Rodolà
-
Greg Ewing
-
Guido van Rossum
-
Jack Diederich
-
M.-A. Lemburg
-
Masklinn
-
Matt Joiner
-
Michael Foord
-
Mike Meyer
-
MRAB
-
Nick Coghlan
-
Paul Colomiets
-
Paul Moore
-
Raymond Hettinger
-
Robert Kern
-
ron3200
-
Stefan Behnel
-
Steven D'Aprano
-
Sven Marnach
-
Terry Reedy