I think I'd favour a namedtuple-style approach, e.g.:
class Color(Enum):
fields = ('RED', 'BLUE', 'GREEN')
(which allows you to build enums programmatically too)
Also, I think you have to provide several classes, at least
SequenceEnum and StringEnum, and perhaps also BitmaskEnum.
It makes sense for them to be separate types, 1) because it makes it
explicit how the given enum behaves, 2) because combining integer and
str constants in an enum would be crazy.
Regards
Antoine.
_______________________________________________
Python-ideas mailing list
Python...@python.org
http://mail.python.org/mailman/listinfo/python-ideas
I partially agree with Antoine; there should be three types of Enum
available:
- sequence (most common: 0, 1, 2, 3, etc.)
- flag (1, 2, 4, 8, etc.)
- unique ('on', 'off'; 'north', 'south', 'east', 'west')
and they shouldn't be mixed together higgledy-piggledy in the same class.
However, I like the simplicity of Tim's approach.
The result of the implementation I've been playing with looks something
like:
class Color(Enum):
type = 'sequence'
RED, GREEN, BLUE
class Geometry(Enum):
type = 'unique'
LEFT, RIGHT, TOP, BOTTOM
WEST, EAST, NORTH, SOUTH
class FieldType(Enum):
type = 'flag'
BINARY, AUTOINC, NULLABLE
The metaclass takes care of assigning the next value and selecting the
proper subclass; 'unique' values are lower-cased (LEFT -> 'left');
subclassing is possible; and each Enum value is properly an instance of
it's Enum class.
One important reason for having the different types of Enum is for
prototyping.
--
~Ethan~
That is too much ' ' typing - I think it would be ok, to have
it like that, but a helper function that would work just like
the namedtuple call:
Color = Enum("Color", "RED GREEN BLUE", int)
The magic of
class Color(Enum):
RED, GREEN, BLUE
is tempting, but basically, what is required so that it does not break
class Color(Enum):
RED = other_module.value
(without making "other_module" a new enum value :-) ) if not worth it,
IMHO.
such magic is implemented in Tim's proof of concept.
A midle term could be to allow declarations like
class Color(IntEnum):
RED, BLUE, GREEN
but with no other logic or expressions in the class body -- all names
showing up are strictly part of the sequence, and them have a constructor call,
with keyword arguments for all the other cases.
>
> Also, I think you have to provide several classes, at least
> SequenceEnum and StringEnum, and perhaps also BitmaskEnum.
> It makes sense for them to be separate types, 1) because it makes it
> explicit how the given enum behaves, 2) because combining integer and
> str constants in an enum would be crazy.
+1
One other important point, that is missed somewhere along Tim's implementation:
the repr and str of the enum values should be the value's name - no need
to the fancy representation. Actually, the fancy representation defeats most
of the purpose of having the constants/enums
They could have a property like Enum._qualified_name if one needs it.
> As far as typos go, I don't think that's a new problem (it certainly isn't> for me, anyway ;) and my best defense is plenty of tests.So with Tim's implementation, what happens here:
if sys.platform == 'win32':
class Color(Enum):
RED, GREEN, BLUE
MAGENTA
and you happen to have no "import sys" in your module? The sys lookup
will succeed, create a new enum, and then you will get an error
something like this:
AttributeError: 'Enum' object has no attribute 'platform'
1) Magic is fun. :)
On 02/12/2013 11:13 AM, Guido van Rossum wrote:
On Tue, Feb 12, 2013 at 10:31 AM, Ethan Furman <et...@stoneleaf.us> wrote:
On 02/12/2013 10:08 AM, Guido van Rossum wrote:
I'm torn. I like the clean look of Tim's:
class Flag(Enum):
RED, WHITE, BLUE
However, I don't like the magic needed for its implementation -- and
anybody who thinks they know Python pretty well and thinks about this
syntax for more than two seconds has to wonder how on earth it's done
(and the answer is pretty horrible). It is also pretty brittle to
depend on the lookup order -- and I suspect it will be prone to
masking other bugs (any typo in a name used in class-level code will
essentially generate a bogus new enum value).
It seems to me the point of an enum is to give names to an order, so why
would the lookup order be a problem?
Because the language requirement that the expressions in a tuple are
evaluated left-to-right is fairly weak. (There are other, similar
contexts where the order is not l2r.)
Personally, I'd be fine with:
class Color(Enum):
BLACK
RED
BLUE
PURPLE
which avoids the l2r issue, and the repetitive use of val() (or whatever it's called).
We could minimize that issue by requiring that enum names be ALLCAPS -- if something comes through that isn't, don't supply a value and let the AttributeError perculate up.As far as typos go, I don't think that's a new problem (it certainly isn't
for me, anyway ;) and my best defense is plenty of tests.
So with Tim's implementation, what happens here:
class Color(Enum):
RED, GREEN, BLUE
if sys.platform == 'win32':
MAGENTA
and you happen to have no "import sys" in your module? The sys lookup
will succeed, create a new enum, and then you will get an error
something like this:
AttributeError: 'Enum' object has no attribute 'platform'
After all the defenses I still don't like Tim's proposed syntax. Color me Barry.
On Tue, Feb 12, 2013 at 11:59 AM, Guido van Rossum <gu...@python.org> wrote:
After all the defenses I still don't like Tim's proposed syntax. Color me Barry.
The main advantage of Tim's implementation syntax (wasn't it Michael Foord who originally proposed it?)
But in 99% of the cases in Python code I absolutely don't care about the values of these enumerations (*), so I'd much, *much* rather write:
class Color(Enum):
RED, BLUE, GREENYes, the implementation is messy, but it can be documented and tested well, and it's fairly small and confined. But this is a matter of preference, and I can see your point (Guido) about preferring the more explicit syntax in order to avoid too much magic.
Eli(*) And where I do care, I'd still enjoy them being generated automatically.
And I'm of the opinion that the most fundamental property of an enum
is that it doesn't print as an int.
On Tue, 12 Feb 2013 12:58:44 -0800I still hope enum values are strings by default - or, if not, that they
Eli Bendersky <eli...@gmail.com> wrote:
> On Tue, Feb 12, 2013 at 11:59 AM, Guido van Rossum <gu...@python.org> wrote:
>
> > After all the defenses I still don't like Tim's proposed syntax. Color me
> > Barry.
> >
>
> In this case, I see no reason not to use Barry's flufl.enum - it's pretty
> good and has been around for a while. The main advantage of Tim's
> implementation syntax (wasn't it Michael Foord who originally proposed it?)
> is that it lets us do less typing which is great. IOW, sure with flufl.enum
> I can do this and be reasonably happy:
>
> class Color(Enum):
> RED = 1
> BLUE = 2
> GREEN = 3
get nice reprs. Integer enums are only useful for interoperability with
stubborn low-level libraries :-)
Guido van Rossum <gu...@python.org> wrote:
> Please. Anything *except*Is a new enum keyword still a possibility? To me that seems to be the
>
> class Color(Enum):
> RED, BLUE, GREEN
>
> And not this eiter:
>
> class Color(Enum):
> RED
> BLUE
> GREEN
cleanest way.
Is a new enum keyword still a possibility? To me that seems to be thecleanest way.I was just thinking the same. We don't like to have more syntax than necessary, but we should also not introduce too much magic...
Frankly, enums are not that useful in small programs. For large
programs or libraries, and especially for public APIs, the extra cost
of defining the enum shouldn't count against them.
Let's just import Barry's enums into the stdlib.
Frankly, enums are not that useful in small programs. For large
programs or libraries, and especially for public APIs, the extra cost
of defining the enum shouldn't count against them.
Let's just import Barry's enums into the stdlib.
Well, that particular case would work (you'd get a NameError: sys) due to having done an attribute lookup on sys, but the following:
class Color(Enum):RED, GREEN, BLUEif platfor == 'win32':MAGENTAwould create an enum value 'platfor'.
What's wrong with enum.make? That just accepts the sequence of names namedtuple style, no values specified anywhere.
Cheers,
Nick.
>
> Tim Delaney
On 13 Feb 2013 09:11, "Tim Delaney" <timothy....@gmail.com> wrote:
>
> On 13 February 2013 09:56, Guido van Rossum <gu...@python.org> wrote:
>>
>> Frankly, enums are not that useful in small programs. For large
>> programs or libraries, and especially for public APIs, the extra cost
>> of defining the enum shouldn't count against them.
>>
>> Let's just import Barry's enums into the stdlib.
>
>
> That's entirely your call. FWIW I probably won't use them, as they fail to meet my needs for an enum, #1 being not having to specify their values in any way if I don't want to. Once you get past 3 or 4 values, it's too easy to miss or reuse a value.What's wrong with enum.make? That just accepts the sequence of names namedtuple style, no values specified anywhere.
What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
different idioms for declaring enums, while a single class-based
definition style should suffice.
Regards
Antoine.
Le Wed, 13 Feb 2013 14:07:48 +1000,
Nick Coghlan <ncog...@gmail.com> a
écrit :
> On 13 Feb 2013 09:11, "Tim Delaney"What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
> <timothy....@gmail.com> wrote:
> >
> > On 13 February 2013 09:56, Guido van Rossum
> > <gu...@python.org> wrote:
> >>
> >> Frankly, enums are not that useful in small programs. For large
> >> programs or libraries, and especially for public APIs, the extra
> >> cost of defining the enum shouldn't count against them.
> >>
> >> Let's just import Barry's enums into the stdlib.
> >
> >
> > That's entirely your call. FWIW I probably won't use them, as they
> > fail
> to meet my needs for an enum, #1 being not having to specify their
> values in any way if I don't want to. Once you get past 3 or 4
> values, it's too easy to miss or reuse a value.
>
> What's wrong with enum.make? That just accepts the sequence of names
> namedtuple style, no values specified anywhere.
different idioms for declaring enums, while a single class-based
definition style should suffice.
Or just a function idiom. We don't have to support the class-based definition style either.
On Feb 13, 2013, at 08:19 AM, Tim Delaney wrote:See, I don't care about that much because I define an enum once, but I *use*
>I'm of the opinion that the most fundamental property of an enum is that it
>can generate its values automatically. Everything else is a nice-to-have,
>but without that property it's not much use as an enum.
it hundreds of times. Thus a little more verbosity/explicitness at definition
time is a one-time cost, and a very low one at that. It might not seems that
way when you're writing them in so many mailing list replies though ;).
In real code, having a really good str and repr, along with good semantics
makes them much more useful for debugging, and very pleasant to use.
On 14 Feb 2013 02:39, "Antoine Pitrou" <soli...@pitrou.net> wrote:
>
> Le Wed, 13 Feb 2013 14:07:48 +1000,
> Nick Coghlan <ncog...@gmail.com> a
> écrit :
> > On 13 Feb 2013 09:11, "Tim Delaney"
> > <timothy....@gmail.com> wrote:
> > >
> > > On 13 February 2013 09:56, Guido van Rossum
> > > <gu...@python.org> wrote:
> > >>
> > >> Frankly, enums are not that useful in small programs. For large
> > >> programs or libraries, and especially for public APIs, the extra
> > >> cost of defining the enum shouldn't count against them.
> > >>
> > >> Let's just import Barry's enums into the stdlib.
> > >
> > >
> > > That's entirely your call. FWIW I probably won't use them, as they
> > > fail
> > to meet my needs for an enum, #1 being not having to specify their
> > values in any way if I don't want to. Once you get past 3 or 4
> > values, it's too easy to miss or reuse a value.
> >
> > What's wrong with enum.make? That just accepts the sequence of names
> > namedtuple style, no values specified anywhere.
>
> What's wrong is that TSBOOWTDI. With enum and enum.make, you have two
> different idioms for declaring enums, while a single class-based
> definition style should suffice.
Really? What happened to the principle of layered API complexity, where a simple *convenience* function is the obvious way to do it for the cases it covers, while the full, more powerful, but also more verbose, explicit subclassing API covers the more complex cases?
Magic namespaces and metaclasses sure don't meet *my* definition of obvious. A normal subclassing API with a convenience function for simple cases? That's just good layered API design.
Cheers,
Nick.
I disagree. enum.make is the more obvious solution, less verbose and IMO nicer looking too. It's explicit that it makes enums, the API is familiar to anyone who has used namedtuple, and it's an expression rather than a statement so it's more flexible. You can't do this with the class-based syntax:
[enum.make(name % i, factory()) for (i, factory) in enumerate(factories)]
Besides, the presence of a second, non-obvious solution is not a violation of One Obvious Way.
I find it amusing that we as a community put so much emphasis on the Zen which ironically includes one of Tim Peter's subtle jokes.
http://bugs.python.org/issue3364
Python is not Perl, but really, there's hardly anything in Python that can't be done two ways if you really try.
--
Steven
One of my common use cases for wanting an enum has always been to map a
third-party protocol or library's error codes.
Really, the one screaming use case in the stdlib is in the errno
module :-) For that you have to be able to define an enum that
subclasses int, though.
I find it slightly amusing that you're complaining about a violation
one of your latest PEPs is trying to make easier to make.
But, still, you are not convincing me that namedtuple is normative in
any way. The fact that many people would prefer a class-based
declarative syntax for namedtuple is proof.
By the way, not only is the repetition of the class name annoying, but
it also makes subclassing more annoying too. Most of my uses of
namedtuple imply subclassing, because I add some behaviour (for
example additional constructors or serializers).
So, compare:
_BaseProtocolItem = namedtuple('_BaseProtocolItem',
('serial_no', 'command'))
class ProtocolItem(_BaseProtocolItem):
# snip custom methods
To the hypothetical:
class ProtocolItem(NamedTuple):
__fields__ = ('serial_no', 'command')
# snip custom methods
And you understand why the namedtuple() factory function is really an
inferior solution with too much typing.
> In which case, they should stop discussing them on python-ideas.
> flufl.enum is a perfectly fine example of normal Pythonic class
> design, with similar examples already in the standard library. The
> approaches being thrown around on this list lately are very cool from
> a technical point of view, and impressive illustrations of what
> Python's metaclass machinery can do for you, but they're also
> completely unintuitive black magic (including "x = ..." having any
> result other than "x is Ellipsis").
You're still ignoring the less-magic solutions such as:
class Errno(Enum):
__enumvalues__ = ('EBADF', 'ENOENT',)
Regards
Antoine.
This is my 5-minute implementation (quite fun)>>> Enum.Colors.red.green.blue<Enum: Colors>>>> Enum.Colors.red<Colors.red = 0>>>> Enum.Colors.green<Colors.green = 1>>>> Enum.Colors.blue<Colors.blue = 2>