Howdy all,
PEP 354: Enumerations in Python has been accepted as a draft PEP. The
current version can be viewed online:
<URL:http://www.python.org/peps/pep-0354.html>
Here is the reStructuredText source as it is today. Please discuss it
here so I can see what issues people may have.
PEP: 354
Title: Enumerations in Python
Version: $Revision: 42186 $
Last-Modified: $Date: 2006-01-26 11:55:20 +1100 (Thu, 26 Jan 2006) $
Author: Ben Finney <be********@benfinney.id.au>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 20-Dec-2005
Python-Version: 2.6
Post-History: 20-Dec-2005
Abstract
========
This PEP specifies an enumeration data type for Python.
An enumeration is an exclusive set of symbolic names bound to
arbitrary unique values. Values within an enumeration can be iterated
and compared, but the values have no inherent relationship to values
outside the enumeration.
Motivation
==========
The properties of an enumeration are useful for defining an immutable,
related set of constant values that have a defined sequence but no
inherent semantic meaning. Classic examples are days of the week
(Sunday through Saturday) and school assessment grades ('A' through
'D', and 'F'). Other examples include error status values and states
within a defined process.
It is possible to simply define a sequence of values of some other
basic type, such as ``int`` or ``str``, to represent discrete
arbitrary values. However, an enumeration ensures that such values
are distinct from any others, and that operations without meaning
("Wednesday times two") are not defined for these values.
Specification
=============
An enumerated type is created from a sequence of arguments to the
type's constructor:: Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') Grades = enum('A', 'B', 'C', 'D', 'F')
Enumerations with no values are meaningless. The exception
``EnumEmptyError`` is raised if the constructor is called with no
value arguments.
The values are bound to attributes of the new enumeration object::
today = Weekdays.mon
The values can be compared::
if today == Weekdays.fri:
... print "Get ready for the weekend"
Values within an enumeration cannot be meaningfully compared except
with values from the same enumeration. The comparison operation
functions return ``NotImplemented`` [#CMP-NOTIMPLEMENTED]_ when a
value from an enumeration is compared against any value not from the
same enumeration or of a different type::
gym_night = Weekdays.wed gym_night.__cmp__(Weekdays.mon)
1 gym_night.__cmp__(Weekdays.wed)
0 gym_night.__cmp__(Weekdays.fri)
-1 gym_night.__cmp__(23)
NotImplemented gym_night.__cmp__("wed")
NotImplemented gym_night.__cmp__(Grades.B)
NotImplemented
This allows the operation to succeed, evaluating to a boolean value::
gym_night = Weekdays.wed gym_night < Weekdays.mon
False gym_night < Weekdays.wed
False gym_night < Weekdays.fri
True gym_night < 23
False gym_night > 23
True gym_night > "wed"
True gym_night > Grades.B
True
Coercing a value from an enumeration to a ``str`` results in the
string that was specified for that value when constructing the
enumeration::
gym_night = Weekdays.wed str(gym_night)
'wed'
The sequence index of each value from an enumeration is exported as an
integer via that value's ``index`` attribute::
gym_night = Weekdays.wed gym_night.index
3
An enumeration can be iterated, returning its values in the sequence
they were specified when the enumeration was created::
print [str(day) for day in Weekdays]
['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
Values from an enumeration are hashable, and can be used as dict
keys::
plans = {} plans[Weekdays.sat] = "Feed the horse"
The normal usage of enumerations is to provide a set of possible
values for a data type, which can then be used to map to other
information about the values::
for report_grade in Grades:
... report_students[report_grade] = \
... [s for s in students if students.grade == report_grade]
Rationale -- Other designs considered
=====================================
All in one class
----------------
Some implementations have the enumeration and its values all as
attributes of a single object or class.
This PEP specifies a design where the enumeration is a container, and
the values are simple comparables. It was felt that attempting to
place all the properties of enumeration within a single class
complicates the design without apparent benefit.
Metaclass for creating enumeration classes
------------------------------------------
The enumerations specified in this PEP are instances of an ``enum``
type. Some alternative designs implement each enumeration as its own
class, and a metaclass to define common properties of all
enumerations.
One motivation for having a class (rather than an instance) for each
enumeration is to allow subclasses of enumerations, extending and
altering an existing enumeration. A class, though, implies that
instances of that class will be created; it is difficult to imagine
what it means to have separate instances of a "days of the week"
class, where each instance contains all days. This usually leads to
having each class follow the Singleton pattern, further complicating
the design.
In contrast, this PEP specifies enumerations that are not expected to
be extended or modified. It is, of course, possible to create a new
enumeration from the string values of an existing one, or even
subclass the ``enum`` type if desired.
Values related to other types
-----------------------------
Some designs express a strong relationship to some other value, such
as a particular integer or string, for each enumerated value.
This results in using such values in contexts where the enumeration
has no meaning, and unnecessarily complicates the design. The
enumerated values specified in this PEP export the values used to
create them, and can be compared for equality with any other value,
but sequence comparison with values outside the enumeration is
explicitly not implemented.
Hiding attributes of enumerated values
--------------------------------------
A previous design had the enumerated values hiding as much as possible
about their implementation, to the point of not exporting the string
key and sequence index.
The design in this PEP acknowledges that programs will often find it
convenient to know the enumerated value's enumeration type, sequence
index, and string key specified for the value. These are exported by
the enumerated value as attributes.
Implementation
==============
This design is based partly on a recipe [#ENUM-RECIPE]_ from the
Python Cookbook.
The PyPI package ``enum`` [#ENUM-PACKAGE]_ provides a Python
implementation of the data types described in this PEP.
References and Footnotes
========================
... [#CMP-NOTIMPLEMENTED]
The ``NotImplemented`` return value from comparison operations
signals the Python interpreter to attempt alternative comparisons
or other fallbacks.
<http://docs.python.org/ref/types.html#l2h-29>
... [#ENUM-RECIPE]
"First Class Enums in Python", Zoran Isailovski,
Python Cookbook recipe 413486
<http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413486>
... [#ENUM-PACKAGE]
Python Package Index, package ``enum``
<http://cheeseshop.python.org/pypi/enum/>
Copyright
=========
This document has been placed in the public domain.
...
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
End:
--
\ Contentsofsignaturemaysettleduringshipping. |
`\ |
_o__) |
Ben Finney 77 3718
Ben Finney wrote: Metaclass for creating enumeration classes ------------------------------------------
The enumerations specified in this PEP are instances of an ``enum`` type. Some alternative designs implement each enumeration as its own class, and a metaclass to define common properties of all enumerations.
One motivation for having a class (rather than an instance) for each enumeration is to allow subclasses of enumerations, extending and altering an existing enumeration. A class, though, implies that instances of that class will be created; it is difficult to imagine what it means to have separate instances of a "days of the week" class, where each instance contains all days. This usually leads to having each class follow the Singleton pattern, further complicating the design.
In contrast, this PEP specifies enumerations that are not expected to be extended or modified. It is, of course, possible to create a new enumeration from the string values of an existing one, or even subclass the ``enum`` type if desired.
Maybe a metaclass implementation complicates design, but usage is quite
simple and flexible. The classics is already referenced by the Python
docs: http://www.python.org/doc/essays/metaclasses/Enum.py
I'm -0 on the PEP.
Kay
Kay Schluehr wrote: The enumerations specified in this PEP are instances of an ``enum`` type. Some alternative designs implement each enumeration as its own class, and a metaclass to define common properties of all enumerations.
One motivation for having a class (rather than an instance) for each enumeration is to allow subclasses of enumerations, extending and altering an existing enumeration. A class, though, implies that instances of that class will be created; it is difficult to imagine what it means to have separate instances of a "days of the week" class, where each instance contains all days. This usually leads to having each class follow the Singleton pattern, further complicating the design.
In contrast, this PEP specifies enumerations that are not expected to be extended or modified. It is, of course, possible to create a new enumeration from the string values of an existing one, or even subclass the ``enum`` type if desired.
Maybe a metaclass implementation complicates design, but usage is quite simple and flexible. The classics is already referenced by the Python docs:
http://www.python.org/doc/essays/metaclasses/Enum.py
Agreed. Allowing subclassing of an existing enum is a strong plus to me.
--
Giovanni Bajo
Ben Finney <bi************@benfinney.id.au> writes: Enumerations with no values are meaningless. The exception ``EnumEmptyError`` is raised if the constructor is called with no value arguments.
Why are empty enumerations not allowed? Empty sets, empty lists,
empty dictionaries are all allowed. I don't see any obvious benefits
to not allowing empty enumerations.
Ben Finney wrote: Values within an enumeration cannot be meaningfully compared except with values from the same enumeration. The comparison operation functions return ``NotImplemented`` [#CMP-NOTIMPLEMENTED]_ when a value from an enumeration is compared against any value not from the same enumeration or of a different type:: [...] This allows the operation to succeed, evaluating to a boolean value::
Given that this is going to change in Python 3.0 for builtin types, I'm not
sure there's much reason to keep it this way. I'd rather a comparison operation
between different enum types to raise an exception. This would be a very
compelling feature to use enum (and in fact, the implementation I chose in
Cookbook for my programs have this feature).
Coercing a value from an enumeration to a ``str`` results in the string that was specified for that value when constructing the enumeration::
>>> gym_night = Weekdays.wed >>> str(gym_night)
'wed'
What's the repr of an enumeration value? OTOH, it should be something like
"Weekdays.wed", so that eval(repr()) holds true. Also, it'd be very useful in
debug dumps, tracebacks and whatnot.
--
Giovanni Bajo
This seems great, except why can't I compare strings? It seems too
useful when dealing with user input, or parsing messages or config
files. Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') Weekdays.mon.__cmp__('mon')
0
Additionaly, perhaps the call method of the enumeration object should
construct a value from strings? Weekdays.mon == Weekdays('mon')
Paul Rubin wrote: Ben Finney <bi************@benfinney.id.au> writes:
Enumerations with no values are meaningless. The exception ``EnumEmptyError`` is raised if the constructor is called with no value arguments.
Why are empty enumerations not allowed? Empty sets, empty lists, empty dictionaries are all allowed. I don't see any obvious benefits to not allowing empty enumerations.
What is an empty enum? How and when would you use it?
The best I can come up with is that an empty enum would
be the enumerated values you have when you don't
actually have any enumerated values. This is not to be
confused with an empty list: an empty list is a
well-defined concept. It is just a list (a container)
with nothing in it. A list of X is like a box
containing X, and like boxes, an empty list (or set, or
dict) is meaningful. An enum of X is just the Xs
themselves. If there is no X, there is nothing there.
But if you have a good usage case for an empty enum,
please feel free to tell us.
--
Steven.
Steven D'Aprano <st***@REMOVEMEcyber.com.au> writes:
Steven D'Aprano <st***@REMOVEMEcyber.com.au> writes: What is an empty enum?
An empty enum is an enum with no identifiers, just like an empty list
is a list with no elements.
How and when would you use it?
The best I can come up with is that an empty enum would be the enumerated values you have when you don't actually have any enumerated values. This is not to be confused with an empty list: an empty list is a well-defined concept. It is just a list (a container) with nothing in it. A list of X is like a box containing X, and like boxes, an empty list (or set, or dict) is meaningful. An enum of X is just the Xs themselves. If there is no X, there is nothing there.
An enum X is a datatype and if the enum is empty then there are no
values for that type. There's nothing wrong with that.
def print_members(header, e): # print header, then members of enum e
print header
for m in e:
print '\t', str(m)
months_longer_than_february = enum('jan', 'mar', 'apr', ) # etc
months_shorter_than_february = enum()
print_members('shorter:', months_shorter_than_february)
print_members('longer:', months_longer_than_february)
should do the obvious thing.
Hmm, I also see the PEP doesn't specify what's supposed to happen with
Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat')
Solar_system = enum('sun', 'mercury', 'venus', 'earth',) # etc.
print Weekdays.sun == Solar_system.sun
so that's another shortcoming with the PEP.
But if you have a good usage case for an empty enum, please feel free to tell us.
Do you have a good usage case for the number
64757429634024117340651643980663421727433660381596 8998799147348150763731 ?
If not, maybe we should modify Python so that all arithmetic operations
throw exceptions if they ever encounter that number. It's unlikely to
ever happen (that number came from os.urandom).
The issue is that Python operations should work uniformly on all values
that make sense. Set operations should work on the empty set. Addition
should work on zero. Enums with no members are similarly perfectly valid
and should work. Kernighan and Plauger in "Software Tools" explained it,
"Make sure your code 'does nothing' gracefully".
Em Seg, 2006-02-27 Ã*s 00:43 -0800, Paul Rubin escreveu: def print_members(header, e): # print header, then members of enum e print header for m in e: print '\t', str(m)
months_longer_than_february = enum('jan', 'mar', 'apr', ) # etc months_shorter_than_february = enum()
print_members('shorter:', months_shorter_than_february) print_members('longer:', months_longer_than_february)
IMHO, you should be using sets, not enums. Something like:
def print_members(header, e):
print header
from m in e:
print '\t', str(e)
months = enum('jan', 'fev', 'mar' ...)
months_longer_than_february = frozenset(months.jan, months.mar,
months.apr ...)
months_shorter_than_february = frozenset()
print_members('shorter:', months_shorter_than_february)
print_members('longer:', months_longer_than_february)
--
"Quem excele em empregar a força militar subjulga os exércitos dos
outros povos sem travar batalha, toma cidades fortificadas dos outros
povos sem as atacar e destrói os estados dos outros povos sem lutas
prolongadas. Deve lutar sob o Céu com o propósito primordial da
'preservação'. Desse modo suas armas não se embotarão, e os ganhos
poderão ser preservados. Essa é a estratégia para planejar ofensivas."
-- Sun Tzu, em "A arte da guerra"
Felipe Almeida Lessa <fe**********@gmail.com> writes: print_members('shorter:', months_shorter_than_february) print_members('longer:', months_longer_than_february)
IMHO, you should be using sets, not enums. Something like:
If enums aren't supposed to work in that construction then the PEP
shouldn't specify that they work that way.
Em Seg, 2006-02-27 Ã*s 02:42 -0800, Paul Rubin escreveu: Felipe Almeida Lessa <fe**********@gmail.com> writes: IMHO, you should be using sets, not enums. Something like:
If enums aren't supposed to work in that construction then the PEP shouldn't specify that they work that way.
Sorry, but where do they say that?
--
"Quem excele em empregar a força militar subjulga os exércitos dos
outros povos sem travar batalha, toma cidades fortificadas dos outros
povos sem as atacar e destrói os estados dos outros povos sem lutas
prolongadas. Deve lutar sob o Céu com o propósito primordial da
'preservação'. Desse modo suas armas não se embotarão, e os ganhos
poderão ser preservados. Essa é a estratégia para planejar ofensivas."
-- Sun Tzu, em "A arte da guerra"
On Mon, 27 Feb 2006 00:43:45 -0800, Paul Rubin wrote: Steven D'Aprano <st***@REMOVEMEcyber.com.au> writes: What is an empty enum? An empty enum is an enum with no identifiers, just like an empty list is a list with no elements.
No, I don't think that is the case. A list is a container. You can take
the elements away and still have the container left. But an enum is not a
container. Take the enumerated values away, and you don't have an empty
enum left, you have nothing left.
All of the machinery of the enum class created by Ben Finney is just to
make sure that red, green and blue behave correctly, without extraneous
string-like or number-like methods. Of course Ben could modify his code so
that enum() returned an object. But to my mind, that would be like
creating an empty integer. Not zero -- zero is a perfectly good integer.
An empty integer, one with no bits. Yes, you could create a wrapper class
that did that. But why would you want to, and even if you did, in what
sense is the result still an integer? How and when would you use it?
The best I can come up with is that an empty enum would be the enumerated values you have when you don't actually have any enumerated values. This is not to be confused with an empty list: an empty list is a well-defined concept. It is just a list (a container) with nothing in it. A list of X is like a box containing X, and like boxes, an empty list (or set, or dict) is meaningful. An enum of X is just the Xs themselves. If there is no X, there is nothing there.
An enum X is a datatype and if the enum is empty then there are no values for that type. There's nothing wrong with that.
Then you would be happy with empty longints that are distinct from zero?
An empty enum() is like a bitless integer.
def print_members(header, e): # print header, then members of enum e print header for m in e: print '\t', str(m)
months_longer_than_february = enum('jan', 'mar', 'apr', ) # etc months_shorter_than_february = enum()
print_members('shorter:', months_shorter_than_february) print_members('longer:', months_longer_than_february)
should do the obvious thing.
Obvious, but meaningless.
Sure, if we were talking about the LIST of months shorter than February,
or the SET of cows with feathers, I would absolutely agree with you. But
think about how enums are handled in other languages, e.g. Pascal and C:
type:
fruit = (banana, apple, orange);
enum { banana, apple, orange } fruit;
As far as I know, neither Pascal nor C allow empty enums. And even if they
did, what and how would you use them for?
type
shortmonth = (); {Months shorter than February}
var
x: shortmonth;
begin
x := ; {empty right hand side of the expression}
Think about the Python usage: fruit = Enum('apple', 'banana', 'orange') snack = fruit[2]
fruit is just a holder, if you will, allowing us to access the enums.
snack == fruit.apple
False snack == fruit.banana
False snack == fruit.orange
True
fruit just exists because of limitations of Python's syntax. What we'd
really like to do is this:
# create enums in the current namespace Enum('apple', 'banana', 'orange') snack = orange snack == apple
False snack == banana
False snack == orange
True
but that is too hard to do in Python. Hence we need some sort of
higher-order object to carry the enums around. But that higher order
object is not an enum, despite the name. The enums are attributes of the
higher order object: apple, banana, orange. fruit is just the scaffolding.
Hmm, I also see the PEP doesn't specify what's supposed to happen with
Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') Solar_system = enum('sun', 'mercury', 'venus', 'earth',) # etc. print Weekdays.sun == Solar_system.sun
so that's another shortcoming with the PEP.
If I recall earlier posts from Ben, enums with the same label from
different enumerations should compare as different.
But if you have a good usage case for an empty enum, please feel free to tell us.
Do you have a good usage case for the number 64757429634024117340651643980663421727433660381596 8998799147348150763731 ?
Of course. If you have a book with
64757429634024117340651643980663421727433660381596 8998799147348150763732
pages, then the last-but-one page will be numbered with the number you are
asking about.
Your example demonstrates that you don't understand my point. I'm not
objecting to any specific group of enums. I'm saying that a group of
enumerated values without any enumerated values is not anything at all.
If not, maybe we should modify Python so that all arithmetic operations throw exceptions if they ever encounter that number. It's unlikely to ever happen (that number came from os.urandom).
The issue is that Python operations should work uniformly on all values that make sense.
Absolutely.
Set operations should work on the empty set.
Correct.
Addition should work on zero.
Agreed 100%.
Enums with no members are similarly perfectly valid and should work.
Nope, not at all. Enums with no members aren't anything at all. They are
like a book with no pages and no cover, a boiled egg without the egg. You
can't shell an egg that isn't there.
Kernighan and Plauger in "Software Tools" explained it, "Make sure your code 'does nothing' gracefully".
Which is excellent advice, for code for which doing nothing is a
meaningful operation. Enums is not one of those cases.
--
Steven.
Felipe Almeida Lessa <fe**********@gmail.com> writes: If enums aren't supposed to work in that construction then the PEP shouldn't specify that they work that way.
Sorry, but where do they say that?
The PEP gives an example of iterating through the members of an enum.
I'm not sure if that's what you're asking, but if you meant something
else, it went past me.
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes: All of the machinery of the enum class created by Ben Finney is just to make sure that red, green and blue behave correctly, without extraneous string-like or number-like methods. Of course Ben could modify his code so that enum() returned an object.
From the PEP: Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat')
...
The values are bound to attributes of the new enumeration object:: today = Weekdays.mon
Looks to me like he already has enum returning an object and he
describes it as an "enumeration object". The values are attributes on
the object. An empty enum simply doesn't have any of those attributes.
But to my mind, that would be like creating an empty integer. Not zero -- zero is a perfectly good integer. An empty integer, one with no bits. Yes, you could create a wrapper class that did that. But why would you want to, and even if you did, in what sense is the result still an integer?
It's more like a set than an integer. You can't iterate through an
integer, but according to the PEP, you can iterate through enums.
Then you would be happy with empty longints that are distinct from zero? An empty enum() is like a bitless integer.
No, I'd say enums are like types. The integer 0 is different from 0.0
which is different from 0+0j and your program can tell them apart.
Similarly you can make e1, e2, and e3 be distinct empty enums.
As far as I know, neither Pascal nor C allow empty enums. And even if they did, what and how would you use them for?
I don't use Pascal. C formerly didn't allow zero-length arrays (I
don't know about now) and that was a design error; a GCC extension
fixes it. Python doesn't make that error: it supports zero-length
arrays (empty lists). If Python fixes C's error about empty arrays it
can also fix C's error about empty enums.
Anyway, Ben's enums already differ from C and Pascal enums in two ways:
1) they are first-class objects, not syntatic sugar
2) You can iterate through them.
That's enough to say their best semantics are probably different from
C's or Pascal's.
Think about the Python usage: fruit = Enum('apple', 'banana', 'orange') snack = fruit[2]
The PEP doesn't have Python enums working like that. Have you
actually read it?
Op 2006-02-27, Steven D'Aprano schreef <st***@REMOVEMEcyber.com.au>: Paul Rubin wrote:
Ben Finney <bi************@benfinney.id.au> writes:
Enumerations with no values are meaningless. The exception ``EnumEmptyError`` is raised if the constructor is called with no value arguments.
Why are empty enumerations not allowed? Empty sets, empty lists, empty dictionaries are all allowed. I don't see any obvious benefits to not allowing empty enumerations.
What is an empty enum? How and when would you use it?
It could come up in a tool where the tool constructs
an identifier list somehow and then an EnumType from
this list.
IMO this is just as usefull as the possibility to do
the following:
for a in []:
...
One could argue that there is no need to loop over an
empty list, because looping over an empty list is a
noop. But it would needlessly complicate code if every
time we wanted to loop over a list we had to code like
this.
if lst:
for a in lst:
Sure as long as we look at Enum as something that will
be something literally provided by the programmer, there
is little use for an Empty Enum. Just as there is little
use for looping over an empty list if you literally
provide that empty list, but maybe somewhere in the future
some automation tool will produce scripts that use Enums.
Why should we possibly make the future production of such
a tool more difficult because we can't imagine the use of
Empty Enums now?
--
Antoon Pardon
>>Why are empty enumerations not allowed? Empty sets, empty lists, empty dictionaries are all allowed. I don't see any obvious benefits to not allowing empty enumerations.
What is an empty enum? How and when would you use it?
It's a Zen thing :) (agh! not the zen thread!) roundSquares = enum() soundsMadeByOneHandClapping = enum() def nirvana(emptyness):
.... if emptyness != enum():
.... throw UnthrowableKoan
....
(okay...maybe it's a little too early on a weekdays.mon morning)
-tkc
Just a couple thoughts: An enumeration is an exclusive set of symbolic names bound to arbitrary unique values.
Uniqueness imposes an odd constraint that you can't have
synonyms in the set: shades = enum({white:100, grey:50, gray:50, black:0})
Not a bad thing, as it would then have interesting results
upon iterating (would the above example have three or four
passes through the iteration loop?), but there could be some
handy uses for enums containing duplicates.
>>> Grades = enum('A', 'B', 'C', 'D', 'F')
This produces the counter-intuitive result of
Grades.A > Grades.B
False
Sure, one can bung with it and create the set of grades
backwards, but then iteration becomes weird. Perhaps the
ability to override the comparitor?
-tkc
> Uniqueness imposes an odd constraint that you can't have synonyms in the set:
>>> shades = enum({white:100, grey:50, gray:50, black:0})
Blast, I hate responding to my own posts, but as soon as I
hit Send, I noticed the syntax here was biffed. Should have
been something like
shades = enum(("white", 100), ("grey", 50),
("gray",50), ("black", 50))
Same could go for days of the week:
dow=enum(("sunday", 0), ("monday", 1),
("start_of_work_week", 1), ... ("friday", 5),
("end_of_work_week", 5)...)
Given more little grey cells (or gray cells?), I'm sure I
could come up with more examples of when it's handy to have
duplicate values in an enum...
-tkc
In article <44**************@REMOVEMEcyber.com.au>,
Steven D'Aprano <st***@REMOVEMEcyber.com.au> wrote: But if you have a good usage case for an empty enum, please feel free to tell us.
I could see empty enums being useful in machine-generated code, perhaps
when generating wrappers around C library APIs. There might be some reason
to exclude certain values that exist in the C API from the Python wrapper.
It's easier to just generate an empty enum than to notice that it's empty
and do something else.
Yeah, I know this is not a very strong argument, but I also can't see any
strong reason to outlaw empty enums either. You can have empty tuples,
lists, sets, and dictionaries. If you count "pass", you can also have
empty code blocks and classes. Why not empty enums? What harm does it do
to allow them?
On 27 feb 2006, at 10:13, Tim Chase wrote: Uniqueness imposes an odd constraint that you can't have synonyms in the set:
> shades = enum({white:100, grey:50, gray:50, black:0}) Blast, I hate responding to my own posts, but as soon as I hit Send, I noticed the syntax here was biffed. Should have been something like shades = enum(("white", 100), ("grey", 50), ("gray",50), ("black", 50))
Same could go for days of the week: dow=enum(("sunday", 0), ("monday", 1), ("start_of_work_week", 1), ... ("friday", 5), ("end_of_work_week", 5)...)
if enum would handle a dict as an input parameter by replacing it with
the dict's iteritems() this is equivalent.
--eric
On Mon, 27 Feb 2006 03:13:08 -0600,
Tim Chase <py*********@tim.thechases.com> wrote: Same could go for days of the week:
dow=enum(("sunday", 0), ("monday", 1),
("start_of_work_week", 1), ... ("friday", 5), ("end_of_work_week", 5)...)
Not really: In some parts of the world, calendar weeks begin on Monday
and end on Sunday, and in other parts of the world, work weeks begin on
Sunday and end on Thursday. ISTM that if you want to do anything with
the value of a given enumeration tag (including make greater/less than
comparisons, sort, etc.), then a dictionary might be better than an
enum. Also, enumeration tags like "end_of_work_week" should be assigned
at run-time based on locale and/or preferences settings (not that you
couldn't have stuck the code above into a preferences file...).
Given more little grey cells (or gray cells?), I'm sure I could come up with more examples of when it's handy to have duplicate values in an enum...
I don't know: If you're using enums as names for otherwise meaningful
values, then you're right. But if you're using enums as meaningful
names for pairwise distinct concepts, then I think otherwise. IMO, if
your program breaks if someone re-assigns the values randomly, then a
better solution than an enum exists. (Or maybe that's because I think
of the tags as nothing more than keywords that work like distinct
values.)
Regards,
Dan
--
Dan Sommers
<http://www.tombstonezero.net/dan/>
"I wish people would die in alphabetical order." -- My wife, the genealogist
"Steven D'Aprano" <st***@REMOVETHIScyber.com.au> wrote A list of X is like a box containing X,
and in another post
A list is a container.
I think it is misleading, if not wrong, to refer to Python collections as
'containers', 'boxes', or similar. A object in a box cannot be in another
disjoint box. A object in a box cannot be in the box a second time. But
both 'cannot's are 'can's for Python objects in respect to Python
collections. So, bad metaphor.
(Yes, CPython implements the association of list positions with objects by
putting an instance/copy of the id/address of the implemented object 'in' a
particular position in a block of memory, but even then, the object itself
in not 'in' the list block. And that is so because of the need for
multiple associations for each object. Beside which, we are discussing the
abstract notion of the empty enumeration.)
You can take the elements away and still have the container left.
I would say instead that you have an empty roster ;-)
I suspect that the notion of empty set was once controversial.
It certainly gives some set theory beginners a pause.
But an enum is not a container.
But neither, I claim, is a list. So to me, you have not drawn a
distrinction, and therefore no justification for different treatment.
To me, the enum proposal is for an immutable ordered set (rather than
multiset) with a friendly interface. So, like other posters, I have no
problem with one that is empty. I also expect enum() to return such
because that is GvR policy for builtin type constructors.
To me, 'I can't think of a use for X' is insufficient reason to prohibit X
in Python. You also need an 'X is tricky/difficult to implement' or 'X is
harmful' claim.
Terry Jan Reedy
Paul Rubin wrote: Hmm, I also see the PEP doesn't specify what's supposed to happen with
Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') Solar_system = enum('sun', 'mercury', 'venus', 'earth',) # etc. print Weekdays.sun == Solar_system.sun
so that's another shortcoming with the PEP.
I think it does, doesn't it? From the PEP:
"Values within an enumeration cannot be meaningfully compared *except
with values from the same enumeration*. The comparison operation
functions return ``NotImplemented`` [#CMP-NOTIMPLEMENTED]_ when a
value from an enumeration is compared against any value not from the
same enumeration or of a different type." (my emphasis)
"I V" <wr******@gmail.com> writes: I think it does, doesn't it? From the PEP:
"Values within an enumeration cannot be meaningfully compared *except with values from the same enumeration*. The comparison operation functions return ``NotImplemented`` [#CMP-NOTIMPLEMENTED]_ when a value from an enumeration is compared against any value not from the same enumeration or of a different type." (my emphasis)
Oh ok, thanks. I didn't understand this before. The link from the
footnote explains it.
"Giovanni Bajo" <no***@sorry.com> writes: Kay Schluehr wrote: Ben Finney wrote: One motivation for having a class (rather than an instance) for each enumeration is to allow subclasses of enumerations, extending and altering an existing enumeration. A class, though, implies that instances of that class will be created; it is difficult to imagine what it means to have separate instances of a "days of the week" class, where each instance contains all days. This usually leads to having each class follow the Singleton pattern, further complicating the design.
Maybe a metaclass implementation complicates design, but usage is quite simple and flexible. The classics is already referenced by the Python docs:
http://www.python.org/doc/essays/metaclasses/Enum.py
Agreed. Allowing subclassing of an existing enum is a strong plus to me.
Having classes as enumerations seems to me to complicate the
conceptual model. As I see it, an enumeration is a container
object. For a container object to be subclassed has no obvious
semantic meaning.
If an enumeration object were to be derived from, I would think it
just as likely to want to have *fewer* values in the derived
enumeration. Subclassing would not appear to offer a simple way to do
that.
To preserve the promise that every enumeration's values are unique,
even the values that were inherited would need to be different in each
enumeration. This, again, doesn't fit well with the concept of
subclassing.
I can see value in wanting to derive a new enumeration from an
existing one; I just don't think subclassing makes sense for that
operation.
How would you specify the interface for deriving one enumeration from
another?
--
\ "Good judgement comes from experience. Experience comes from |
`\ bad judgement." -- Frederick P. Brooks |
_o__) |
Ben Finney
Paul Rubin <http://ph****@NOSPAM.invalid> writes: Ben Finney <bi************@benfinney.id.au> writes: Enumerations with no values are meaningless. The exception ``EnumEmptyError`` is raised if the constructor is called with no value arguments.
Why are empty enumerations not allowed? Empty sets, empty lists, empty dictionaries are all allowed. I don't see any obvious benefits to not allowing empty enumerations.
By way of explanation, my earlier implementations of this didn't have
enumerations as sequences. Now that the design has converged more on a
sequence and container idiom, I agree with you.
I'll amend the specification so empty enumerations are not an
error. (This will also remove the last remaining exception class from
the specification, making it cleaner still. Good.)
--
\ "I love to go down to the schoolyard and watch all the little |
`\ children jump up and down and run around yelling and screaming. |
_o__) They don't know I'm only using blanks." -- Emo Philips |
Ben Finney
Paul Rubin <http://ph****@NOSPAM.invalid> writes: Hmm, I also see the PEP doesn't specify what's supposed to happen with
Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') Solar_system = enum('sun', 'mercury', 'venus', 'earth',) # etc. print Weekdays.sun == Solar_system.sun
so that's another shortcoming with the PEP.
The PEP text specifies::
Values within an enumeration cannot be meaningfully compared
except with values from the same enumeration. The comparison
operation functions return ``NotImplemented`` when a value from an
enumeration is compared against any value not from the same
enumeration or of a different type
The Python parser knows what to do when a comparison returns
NotImplemented.
--
\ "It ain't so much the things we don't know that get us in |
`\ trouble. It's the things we know that ain't so." -- Artemus |
_o__) Ward (1834-67), U.S. journalist |
Ben Finney
"Terry Reedy" <tj*****@udel.edu> writes: "Steven D'Aprano" <st***@REMOVETHIScyber.com.au> wrote A list is a container. I think it is misleading, if not wrong, to refer to Python collections as 'containers', 'boxes', or similar. A object in a box cannot be in another disjoint box. A object in a box cannot be in the box a second time. But both 'cannot's are 'can's for Python objects in respect to Python collections. So, bad metaphor.
Thanks. Earlier today I referred to my conceptual model for
enumerations as "container". I agree with you that should be amended
to "collection".
Since an empty enumeration is consistent with the "collection" idiom,
and has obvious behaviour, I see no problem changing the specification
to remove that restriction.
To me, 'I can't think of a use for X' is insufficient reason to prohibit X in Python.
I'm finding that most of the changes I made to the specification
before submitting the initial draft were removing such
restrictions. I'm glad to see that continues :-)
--
\ "Either he's dead or my watch has stopped." -- Groucho Marx |
`\ |
_o__) |
Ben Finney
Steven D'Aprano wrote: Paul Rubin wrote:
What is an empty enum? How and when would you use it?
The best I can come up with is that an empty enum would be the enumerated values you have when you don't actually have any enumerated values. This is not to be confused with an empty list: an empty list is a well-defined concept. It is just a list (a container) with nothing in it. A list of X is like a box containing X, and like boxes, an empty list (or set, or dict) is meaningful. An enum of X is just the Xs themselves. If there is no X, there is nothing there.
"Nothing" in python is None. If empty enum is None, then it is the same
as empty list in Lisp is nil, i.e. type-of(nil)=='list. Giving a type
to nothing, isn't really useful. Thus, empty enum is needed.
Programaticaly or, say, in implementation, enum is some structure that
holds some stuff. Just like tuples and lists are structures to hold
something. As a structure, it can be empty.
Ben Finney <bi****************@benfinney.id.au> writes: If an enumeration object were to be derived from, I would think it just as likely to want to have *fewer* values in the derived enumeration. Subclassing would not appear to offer a simple way to do that.
pentium_instructions = enum('add', 'sub', 'mul', ) # etc
athlon64_instructions = enum('add64', 'sub64', # etc
base_enum=pentium_instructions)
# 386 has no floating point unit
i386_instructions = enum(base_enum=pentium_instructions,
remove=('addf', 'subf', 'mulf',)) # etc
"Giovanni Bajo" <no***@sorry.com> writes: Ben Finney wrote: Values within an enumeration cannot be meaningfully compared except with values from the same enumeration. The comparison operation functions return ``NotImplemented`` [#CMP-NOTIMPLEMENTED]_ when a value from an enumeration is compared against any value not from the same enumeration or of a different type:: [...] This allows the operation to succeed, evaluating to a boolean value:: Given that this is going to change in Python 3.0 for builtin types, I'm not sure there's much reason to keep it this way.
What is it that will change? Can you point to something we should read
about this?
I'd rather a comparison operation between different enum types to raise an exception. This would be a very compelling feature to use enum (and in fact, the implementation I chose in Cookbook for my programs have this feature).
A previous implementation raised exceptions from failed comparisons.
Here is the discussion that convinced me that was not the right thing
to do::
<URL:http://groups.google.com/group/comp.lang.python/browse_thread/thread/140de1765bbb8d2d/> Coercing a value from an enumeration to a ``str`` results in the string that was specified for that value when constructing the enumeration::
>>> gym_night = Weekdays.wed >>> str(gym_night) 'wed'
What's the repr of an enumeration value?
Hmm, the PEP doesn't specify. I was leaving it up to the
implementation.
OTOH, it should be something like "Weekdays.wed", so that eval(repr()) holds true. Also, it'd be very useful in debug dumps, tracebacks and whatnot.
An enumeration value object knows the enumeration object that created
it, but that enumeration doesn't know its own name; objects don't know
their own name, since they could have many, or none.
Even if an enumeration value were to know the name of the enumeration
that created it, you still have this problem:: Colours = enum('red', 'green', 'blue') repr(Colours.green)
Colours.green Lamps = Colours repr(Lamps.red)
Colours.red
Is this desirable behaviour? I don't think so.
--
\ "I went to the hardware store and bought some used paint. It |
`\ was in the shape of a house." -- Steven Wright |
_o__) |
Ben Finney
Tim Chase <py*********@tim.thechases.com> writes: Just a couple thoughts:
Appreciated. An enumeration is an exclusive set of symbolic names bound to arbitrary unique values.
Uniqueness imposes an odd constraint that you can't have synonyms in the set:
>>> shades = enum({white:100, grey:50, gray:50, black:0})
I think a dict is best used for this purpose. An enumeration is for
when you want all the values in the collection to be unique, but don't
care particularly what those values *are*.
> >>> Grades = enum('A', 'B', 'C', 'D', 'F')
This produces the counter-intuitive result of
>>> Grades.A > Grades.B False
Sure, one can bung with it and create the set of grades backwards, but then iteration becomes weird. Perhaps the ability to override the comparitor?
Again, if you actually want specific values associated with each
member of the collection, I think a dict is best.
Note that the enumeration values *can* be dict keys, so you could
associate the values with other objects that way.
--
\ "I was in a bar the other night, hopping from barstool to |
`\ barstool, trying to get lucky, but there wasn't any gum under |
_o__) any of them." -- Emo Philips |
Ben Finney
[fixing References field. Please reply in the original thread so I can
find you easily.]
Roy Smith <ro*@panix.com> writes: If I have an enum, how can I verify that it's a legal value? Can I do:
Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat')
def foo (day): if day not in Weekdays: raise ValueError
The current PEP text doesn't specify; that's an omission on my
part. The answer should be "yes, an enum can be tested for
membership".
I'll add that to the specification.
Also, do enums have __dict__ slots?
Not specified as having one, no.
Can I do something like:
day = 'sun' print Weekdays.__dict__[day]
You can do the same thing more elegantly by explicitly querying the
attributes of the enumeration:: day = 'sun' print getattr(Weekdays, day)
sun
--
\ "There was a point to this story, but it has temporarily |
`\ escaped the chronicler's mind." -- Douglas Adams |
_o__) |
Ben Finney
Ben Finney <bi****************@benfinney.id.au> writes: By way of explanation, my earlier implementations of this didn't have enumerations as sequences. Now that the design has converged more on a sequence and container idiom, I agree with you.
say that
Weekdays = enum('mon', 'tue', ...)
What is the type of Weekdays.mon supposed to be?
Do you anticipate having parameters like socket.AF_INET that are
currently integers, become enumeration members in future releases?
In article <87************@rose.polar.local>,
Ben Finney <bi****************@benfinney.id.au> wrote: I'll amend the specification so empty enumerations are not an error. (This will also remove the last remaining exception class from the specification, making it cleaner still. Good.)
And once you've done that, we can build an enum of all the exceptions which
enums can raise :-)
A few random questions:
a = enum ('foo', 'bar', 'baz')
b = enum ('foo', 'bar', 'baz')
what's the value of the following:
a == b
a is b
a.foo == b.foo
a.foo is b.foo
len (a)
str (a)
repr (a)
hash (a)
type (a)
Can you make an enum from a sequence?
syllables = ['foo', 'bar', 'baz']
c = enum (syllables)
You imply that it works from "An enumerated type is created from a sequence
of arguments to the type's constructor", but I suspect that's not what you
intended.
BTW, I think this is a great proposal; enums are a badly needed part of the
language. There's been a number of threads recently where people called
regex methods with flags (i.e. re.I) when integers were expected, with
bizarre results. Making the flags into an enum would solve the problem
while retaining backwards compatibility. You would just have to put in the
re module something like:
flags = enum ('I', 'L', 'M', 'S', 'U', 'X')
I = flags.I
L = flags.L
M = flags.M
S = flags.S
U = flags.U
X = flags.X
then the methods which expect a flag can do:
if flag is not in re.flags:
raise ValueError ("not a valid flag")
and the ones which expect an integer would (if nothing better), raise
NotImplemented when they tried to use the value as an integer if you passed
it a flag by mistake.
Terry Reedy <tj*****@udel.edu> wrote:
... I suspect that the notion of empty set was once controversial.
Yep: Reverend Dodgson (best known by his pen name of Lewis Carroll, and
as the author of the Alice novels, but a logician and mathematician IRL)
fought long and hard against Cantor's set theory, focusing on "the empty
set" (singular: in Cantor's theory there can be only one) but not
managing to build an actual contradiction around it. The poor man died
just before his compatriot Bertrand Russell found "Russell's Paradox"
(which destroyed Frege's logic -- but is quite applicable to destroying
a foundation stone of Cantor's set theory as well).
I personally believe that Dodgson was reaching towards what today we
call modal logic (particularly intensional logic), though he could
hardly get there with such encumbrances as his fiction writing, his
photography, his heavy smoking of cannabis, etc, etc. But, that's just
me, and I can't claim to be an expert at modern set theory (though I do
know enough of it to see that it's quite different from Cantor's "naive"
version that's still taught in most schools...!-), much less of the
subtleties of modal logic. Still, if you give a set interpretation of
modal logic, there isn't ONE empty set: the crucial point of modal
logic, from my semi-learned POV, is that it distinguishes what JUST
HAPPENS to be false, from what MUST INTRINSICALLY be false (and ditto
for true). In set terms, say, "all integers x such that x>x" would be
an *intrinsically* empty set, while "all dogs that are in this house
right now" would be a set which *just happens* to be empty -- they
aren't "one and the same, the sole empty set" any more than they are in
commonsense (the notion Dodgson fought against).
Alex
Ben Finney <bi****************@benfinney.id.au> wrote:
... The Python parser knows what to do when a comparison returns NotImplemented.
The parser has nothing to do with it, but the bytecode interpreter sure
does;-).
Alex
Ben Finney wrote: This PEP specifies an enumeration data type for Python.
-1 for this particular proposal as a builtin. I feel it's not a great
design of something that isn't terribly useful to begin with. +0 for
the standard library, only after a couple changes. As I see it, this
whole proposal is a minor improvement over doing something like the
following, but too minor an improvement to add to the builtin
namespace:
MON = 'mon'
TUE = 'tue'
etc.
DAYS_OF_WEEK = (MON,TUE,etc.)
[snip] It is possible to simply define a sequence of values of some other basic type, such as ``int`` or ``str``, to represent discrete arbitrary values. However, an enumeration ensures that such values are distinct from any others, and that operations without meaning ("Wednesday times two") are not defined for these values.
Here's why I think it's not too useful to begin with: the benefits of
the enum you describe here are pretty weak.
It's a pretty weak case to have a dedicated builtin to prevent
duplicates in something that changes maybe once a month, as enums tend
to change rather slowly. (At least, that's the way enums in other
languages are used, and the design you present here seems to suggest
you intend to use them that way as well.) And frankly, a unit test or
assertion could check this.
As for preventing nonsensical operations on enum constants: good idea,
but let's face it: a decent programmer knows whether he or she's
dealing with a enum value or a piece of string data. This is why I
said it's not terribly useful (as opposed to not useful at all).
Unfortunately, this proposal fails to prevent a nonsensical operation
in the case where it would be most useful to do so.
[snip] Enumerations with no values are meaningless. The exception ``EnumEmptyError`` is raised if the constructor is called with no value arguments.
No strong feeling on this issue. Probably no use for an empty enum,
but no harm either.
[snip] This allows the operation to succeed, evaluating to a boolean value::
>>> gym_night = Weekdays.wed >>> gym_night < Weekdays.mon False >>> gym_night < Weekdays.wed False >>> gym_night < Weekdays.fri True >>> gym_night < 23 False >>> gym_night > 23 True >>> gym_night > "wed" True >>> gym_night > Grades.B True
The nonsensical comparisions should throw value errors. You say that
you want to get rid of operations that don't make sense, but it seems
misguided that we would get rid of multiplying by a integer (something
that's not likely to happen much at all) but retain comparison with
other types (something that's going to be much more common).
I realize that this is a thoughtful decision on your part, that you
have some reason for it and were not just blindly following the example
of other Python builtins. However, IMHO, this design decision forsakes
the single most useful feature of an enum, and replaces it with
confusion.
[snip examples of use]
One thing I'd add is something like Weekdays.get_constant("wed"). I
realize that you can do this with getattr, but you can also do
getattr(Weekdays,"__dict__") with getattr, and you ought to have a
method that thows and exception if you try to access the __dict__
constant.
Also, it blesses the operation, so that people who are trying to access
the constant after reading its name from the database don't have to
feel guilty about using getattr.
[snip] Metaclass for creating enumeration classes ------------------------------------------
The enumerations specified in this PEP are instances of an ``enum`` type. Some alternative designs implement each enumeration as its own class, and a metaclass to define common properties of all enumerations.
One motivation for having a class (rather than an instance) for each enumeration is to allow subclasses of enumerations, extending and altering an existing enumeration. A class, though, implies that instances of that class will be created;
How so? This is true of classes with metaclass type, but using a
different metaclass suggests maybe it isn't like a regular class. In
particular, constructing an instance could be a non-op; instead the
class creates it's own instances at definition time. I don't see
anything that implies that a class can't do that.
it is difficult to imagine what it means to have separate instances of a "days of the week" class, where each instance contains all days.
I don't understand what you're saying here. It doesn't seem to me hard
to imagine that a class could create its own instances, but I'm not
sure if that's what you're talking about.
This usually leads to having each class follow the Singleton pattern, further complicating the design.
Meh. I don't feel too strongly about this, but I'd think an enum as a
class is better, because you can't (or, rather, oughtn't) compare
constants from different enums. That's a logical demarcation of
classes.
Sorry if I was a little blunt here. In the end, I really don't see the
problems that this enum addresses being all that common; it seems a lot
of work for minor benefit.
Carl Banks
Tim Chase wrote: ... throw UnthrowableKoan ...
(okay...maybe it's a little too early on a weekdays.mon morning)
Probably, since a SyntaxError slipped in. Throw is C++ Tim.
It's "raise UnRaisableKoan".
on 28.02.2006 07:50 Carl Banks said the following: Ben Finney wrote: This PEP specifies an enumeration data type for Python.
[snip] Here's why I think it's not too useful to begin with: the benefits of the enum you describe here are pretty weak.
I need to disagree heavily here :)
+1 from me for the general idea of a builtin enum.
(and for 'real world' use cases: I use this type of enum, the PEP one,
in my code regularly)
It's a pretty weak case to have a dedicated builtin to prevent duplicates in something that changes maybe once a month, as enums tend to change rather slowly. (At least, that's the way enums in other languages are used, and the design you present here seems to suggest you intend to use them that way as well.) And frankly, a unit test or assertion could check this.
[snip]
I don't understand what you mean by 'change rather slowly'?
The dominant use case for an explicit enum is to make it clear for the
code user that the values are a separate type, and prevent errors
occurring because the abused underlying type shows through (be it
strings or integers) or at least give informative warnings for them. If
you want more than that something, like a dict, will probably be better.
recent examples from this list:
2006-01-03: http://www.nabble.com/Re%3A-Regex-anomaly-p2179421.html
2006-02-20: http://www.nabble.com/Re%3A-Regular-...-p3029028.html
The nonsensical comparisions should throw value errors.
That's a valid point.
I hope someone knowledgeable will go through the standard library and
check each flag-like thing (which is not directly dependent on an
underlying c-library idiom) against the proposed enum type.
One thing that is probably missing to allow this, is a enum-set-creation
with the | operator::
Weekdays = enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
daysihate = Weekdays.mon | Weekdays.thu
(and this discussion needs to move to python-dev ?)
As for the metaclass versions: For myself, the above version feels more
natural and straightforward (in the same way as the PEP author describes
it), though I understand the subclassing ideas.
But are there use cases for subclassing, that aren't better served with
a new enum or something homegrown?
Can C++/Pascal/Java enums be subclassed?
cheers,
stefan
Stefan Rank wrote: on 28.02.2006 07:50 Carl Banks said the following: Ben Finney wrote: This PEP specifies an enumeration data type for Python. [snip] Here's why I think it's not too useful to begin with: the benefits of the enum you describe here are pretty weak.
I need to disagree heavily here :)
"the benefits of the enum you describe here [beyond my example which I
claimed enum was only a minor improvement over] are pretty weak." It's a pretty weak case to have a dedicated builtin to prevent duplicates in something that changes maybe once a month, as enums tend to change rather slowly. (At least, that's the way enums in other languages are used, and the design you present here seems to suggest you intend to use them that way as well.) And frankly, a unit test or assertion could check this. [snip]
I don't understand what you mean by 'change rather slowly'?
Construct data structure on-the-fly from an XML file edited by multiple
people every day = changes rather quickly
Construct data structure from a Python file that was last edited a year
and a half ago = changes rather slowly
Typically, enums fall into the latter category. You set the enum
values, and then pretty much leave them alone, adding new values only
occasionally. (Come on, how often do the days of the week change?)
All I was saying is, changes to the enum values are infrequent enough
that having a special type just to make sure there are no duplicates is
a waste. The only justification for a built-in enum is the other stuff
you mentioned.
One thing that is probably missing to allow this, is a enum-set-creation with the | operator::
Weekdays = enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun') daysihate = Weekdays.mon | Weekdays.thu
(and this discussion needs to move to python-dev ?)
What's wrong with set((Weekdays.mon,Weekdays.thu))? Explicit is better
than implicit.
As for the metaclass versions: For myself, the above version feels more natural and straightforward (in the same way as the PEP author describes it), though I understand the subclassing ideas.
But are there use cases for subclassing, that aren't better served with a new enum or something homegrown? Can C++/Pascal/Java enums be subclassed?
In C++, enum is a type but not a class. Same thing with Ada. Java
didn't have enums last time I checked. Don't know about Pascal. I
didn't care too much about subclassing; I just thought different enum
constant that couldn't (or, rather, oughtn't) be compared probably
should be instances of a separate class. It doesn't matter much,
though.
Should something like this work:
day = Weekdays.mon
isinstance(day,Weekdays)
?
Carl Banks
It seems the concensus is that empty enums should be allowed for
consistancy, and to support the loop that doesn't. I thought I'd find
some data points in other languages as a guide:
* C - builtin, empty enumerations not allowed
* C++ - builtin, empty enumerations allowed. C++ doesn't have
iteration over a enumeration, except as a side-effect of direct
translation from elements to integerts (and then, only for a
sequentially assigned enumeration). An enumeration is a type, so it
can be thrown (raised) as an exception - http://oopweb.com/CPP/Documents/CPPA...html#EMPTYENUM
* Java - builtin, empty enumerations allowed previously to 1.5, but
maybe not after - http://bugs.sun.com/bugdatabase/view...bug_id=5081785
* OCaml - library, empty enumerations allowed as a consequence of being
able to add and remove enumeration elememts: http://ocaml-lib.sourceforge.net/doc/Enum.html
I realize this is a strange and short list, but it's all I can do with
5 min of Google and a little testing.
on 28.02.2006 12:14 Carl Banks said the following:
[snip] It's a pretty weak case to have a dedicated builtin to prevent duplicates in something that changes maybe once a month, as enums tend to change rather slowly. (At least, that's the way enums in other languages are used, and the design you present here seems to suggest you intend to use them that way as well.) And frankly, a unit test or assertion could check this. [snip]
I don't understand what you mean by 'change rather slowly'?
Construct data structure on-the-fly from an XML file edited by multiple people every day = changes rather quickly
Construct data structure from a Python file that was last edited a year and a half ago = changes rather slowly
Typically, enums fall into the latter category. You set the enum values, and then pretty much leave them alone, adding new values only occasionally. (Come on, how often do the days of the week change?) All I was saying is, changes to the enum values are infrequent enough that having a special type just to make sure there are no duplicates is a waste. The only justification for a built-in enum is the other stuff you mentioned.
agreed One thing that is probably missing to allow this, is a enum-set-creation with the | operator::
Weekdays = enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun') daysihate = Weekdays.mon | Weekdays.thu
(and this discussion needs to move to python-dev ?)
What's wrong with set((Weekdays.mon,Weekdays.thu))? Explicit is better than implicit.
agreed again.
the | idea would only be for (interface) backwards compatibility. As for the metaclass versions: For myself, the above version feels more natural and straightforward (in the same way as the PEP author describes it), though I understand the subclassing ideas.
But are there use cases for subclassing, that aren't better served with a new enum or something homegrown? Can C++/Pascal/Java enums be subclassed?
In C++, enum is a type but not a class. Same thing with Ada. Java didn't have enums last time I checked. Don't know about Pascal.
Was more of a question for subclassing use cases in other languages.
BTW Java has an enum now, which cannot be subclassed either AFAIK. And
it's the same for (Delphi) Pascal.
I didn't care too much about subclassing; I just thought different enum constant that couldn't (or, rather, oughtn't) be compared probably should be instances of a separate class. It doesn't matter much, though. Should something like this work:
day = Weekdays.mon isinstance(day,Weekdays)
?
I think in the PyPI package `type(Weekdays)` is `Enum` and
`type(Weekdays.mon)` is `EnumValue`, so this would not work.
But membership testing `if day in Weekdays: ...` could do the same, and
type-checking for enum values `isinstance(day, EnumValue)` would work
(might be unpythonic though).
In the cookbook recipe `enum('..')` is a function and constructs two new
types on the fly, so the values of two different enums would be of a
different type, but you would not be able to name it easily...
cheers
Ben Finney wrote: This PEP specifies an enumeration data type for Python.
An enumeration is an exclusive set of symbolic names bound to arbitrary unique values. Values within an enumeration can be iterated and compared, but the values have no inherent relationship to values outside the enumeration.
-1 on the proposal as-is. I don't have many use cases for enumerations,
and I don't think they merit appearing in the builtins. If you put them
in the collections module instead, I'd probably be +0.
This allows the operation to succeed, evaluating to a boolean value::
>>> gym_night = Weekdays.wed >>> gym_night < Weekdays.mon False >>> gym_night < Weekdays.wed False >>> gym_night < Weekdays.fri True >>> gym_night < 23 False >>> gym_night > 23 True >>> gym_night > "wed" True >>> gym_night > Grades.B
True
For the few cases of enumerations that I've needed, I've never wanted
them to be comparable with <, >, etc. If there were two classes, say
``collections.Enum`` and ``collections.OrderedEnum`` where only the
latter made the enumerated items comparable, you might even get me as
high as +0.5. (I only care about the non-comparable one, but I
understand that others may have a need for the comparable one.)
STeVe
Ben Finney wrote: PEP 354: Enumerations in Python has been accepted as a draft PEP. The current version can be viewed online:
-1 on the proposal as a builtin, -0 on including any kind of
enumeration in the standard library unless I can see a compelling use
case; Carl Banks' approach seems more flexible and doesn't add yet more
to the burgeoning Python distro.
Add me to the "me, too!" list of people who think enumerations would be
better off without > or < comparison.
On Monday 27 February 2006 02:49, Ben Finney wrote: Coercing a value from an enumeration to a ``str`` results in the string that was specified for that value when constructing the enumeration::
That sentence seems to assume that all enumeration values will have been
specified as strings. Thats reasonable, but your description of the creation
of an enumeration doesnt specify that.
An enumerated type is created from a sequence of arguments to the type's constructor::
>>> Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') >>> Grades = enum('A', 'B', 'C', 'D', 'F')
s/arguments/strings/
?
--
Toby Dickenson
"Stefan Rank" <st*********@ofai.at> wrote in message
news:44**************@ofai.at... recent examples from this list:
2006-01-03: http://www.nabble.com/Re%3A-Regex-anomaly-p2179421.html 2006-02-20: http://www.nabble.com/Re%3A-Regular-...-p3029028.html
If the re flags were implemented as instances of object instead of int,
then misuse of them as int args would be excepted. I don't know if such a
change would otherwise cause a problem.
I wonder whether a subclass of object (EnumSet?) that allowed for
initialization with a better string representation and that disabled order
comparisons would fill the bill for unordered enum.
As Steven Bethard also noted, there seem to be a need for two Enum
subclasses:
EnumSet and EnumSeq.
Terry Jan Reedy
On Tue, 28 Feb 2006 14:26:49 -0500
"Terry Reedy" <tj*****@udel.edu> wrote: "Stefan Rank" <st*********@ofai.at> wrote in message news:44**************@ofai.at... recent examples from this list:
2006-01-03: http://www.nabble.com/Re%3A-Regex-anomaly-p2179421.html 2006-02-20: http://www.nabble.com/Re%3A-Regular-...-p3029028.html
If the re flags were implemented as instances of object instead of int, then misuse of them as int args would be excepted. I don't know if such a change would otherwise cause a problem.
I wonder whether a subclass of object (EnumSet?) that allowed for initialization with a better string representation and that disabled order comparisons would fill the bill for unordered enum.
As Steven Bethard also noted, there seem to be a need for two Enum subclasses: EnumSet and EnumSeq.
And one other distinction -- mutable versus immutable. A
immutable enum is the usual use case, a mutable one is a
"vocabulary" (i.e. an open set of symbols, which
nevertheless requires controlled access).
The key thing about symbols like enums etc is that they
don't really represent anything (or that which they
represent is inconvenient to bind to the symbols -- perhaps
its even an abstract concept that has no programmatic
representation beyond the enumerated value).
One thing about an enumerated value is that it should know
what enum it belongs to. For example, I'm currently working
on a project that has the following rather unfortunate
near-collision of symbols:
sym.ABSTRACTS "scope of abstract nouns" in
SCOPE vocabulary
sym.ABSTR "definiteness of abstract noun" in
ARTICLE enum
sym.ABST "number of noun which has no
count because abstract (not mass)"
NUMBER enum
It's quite useful that I can ask "sym.ABST" which one of
the above domains it's in: sym.ABST.domain
NUMBER
or even better:
sym.ABST
<!NUMBER: ABST>
Which makes the enumeration values somewhat
self-documenting. I'm currently trying to figure out how
best to make this instead show (e.g.):
sym.ABST
<!NUMBER: ABST - Noun uncountable because abstract.>
I can of course, also use this to catch errors by checking
that the pluralization function in my locale module has
been passed a NUMBER or an explicit int, instead of say, a
SCOPE, by accident.
Another feature here is that all of these symbols, though
also defined within their domain collections is also made
into an attribute of the sym object (which is a "trivial
class" or "convenience namespace").
I also raise an error if any domain tries to overwrite a
symbol in sym, so I can avoid accidental collisions.
I'm still tinkering with this, and the above is from memory,
but I do have it working on another machine. I'm currently
subclassing from dict, not set, though I'm unsure if this is
really wise (but I'm more familiar with dict, and I'm
currently making use of the mapping internally, though I'm
not sure I really need to).
I'm not sure this would be a smart way to do this in another
application, but it looks promising for this one (which is
a kind of artificial locale-neutral language
representation).
I'm not sure that the enum described in the PEP would be as
useful to me as this. So I'm -0 on it becoming a built-in,
though I'm +0 on it becoming a module (it's not that hard
to type "from enum import Enum"). But I'm not sure it's the
"best" enum, or even that "one size fits all" with enums
(though having fewer implementations might improve clarity
if they are really equivalent).
Cheers,
Terry
--
Terry Hancock (ha*****@AnansiSpaceworks.com)
Anansi Spaceworks http://www.AnansiSpaceworks.com
On 28 Feb 2006 09:58:47 -0800
"sj*******@yahoo.com" <sj*******@yahoo.com> wrote: Add me to the "me, too!" list of people who think enumerations would be better off without > or < comparison.
+1 on "unordered"
Comparable enums do have use-cases (like the days of the
week), but I can't think of many of them. Also, there is
the point that even the days of the week is not a great
use of the ordering since it varies between locales, as
I think was pointed out in this thread.
The most compelling reason to have some kind of ordering is
just so you can iterate over them -- but dicts are
iterable without being ordered, so I see no reason
why enums couldn't be.
In C, enums are a rather shallow mask pulled over integers,
and as is common practice in C, the mask is routinely
ignored or removed. I think that enums in Python, if added,
should be "strong enums" without this kind of ambiguous
behavior.
I also think though that the characterization of the
behavior of enumerated value instances is much more
important than the behavior of the enumeration collection
object, which is basically just a set, anyway.
Cheers,
Terry
--
Terry Hancock (ha*****@AnansiSpaceworks.com)
Anansi Spaceworks http://www.AnansiSpaceworks.com This discussion thread is closed Replies have been disabled for this discussion. |