PEP 354: Enumerations in Python

S

Stefan Rank

Ben said:
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:-Regex-anomaly-p2179421.html
2006-02-20:
http://www.nabble.com/Re:-Regular-expression-gone-mad-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
 
C

Carl Banks

Stefan said:
Ben said:
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
 
S

Stefan Rank

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.
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
 
J

JW

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/CPPAnnotations/Volume/cplusplus16.html#EMPTYENUM
* Java - builtin, empty enumerations allowed previously to 1.5, but
maybe not after -
http://bugs.sun.com/bugdatabase/view_bug.do?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.
 
S

Steven Bethard

Ben said:
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::

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
 
S

sjdevnull

Ben said:
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.
 
T

Toby Dickenson

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::

s/arguments/strings/

?
 
T

Terry Reedy

Stefan Rank said:

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
 
T

Terry Hancock

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:
NUMBER

or even better:
<!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.):
<!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
 
T

Terry Hancock

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
 
B

Ben Finney

Paul Rubin said:
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

These don't seem simple or elegant. I don't see a particular benefit
to doing it that way, rather than being explicit about manipulating
the member list::

pentium_instructions = enum('add', 'sub', 'mul')
athlon64_instructions = enum(
*[ str(i) for i in pentium_instructions] + ['add64', 'sub64'] )
i386_instructions = enum(
*[ str(i) for i in pentium_instructions
if i not in ['addf', 'subf', 'mulf'] ] )

I don't see a benefit to having the enum constructor grow extra
keyword arguments for operating on lists, when a list has its own
methods for doing so.
 
B

Ben Finney

Paul Rubin said:
say that
Weekdays = enum('mon', 'tue', ...)

What is the type of Weekdays.mon supposed to be?

The specification doesn't mention that; it should.

I'm calling them EnumValue, but that makes it difficult to talk
about. The term "value" is commonly used to refer to an instance of a
type, so "enumeration value" sounds like an instance of the type
"enumeration", which is confusing.

Perhaps, since "collection" seems the right metaphor for an
enumeration, each of the values in an enumeration should be
EnumMember. On the assumption these might be basic types, though, that
name doesn't read so easily in lowercase ('enummember').

Thoughts?
 
B

Ben Finney

Paul Rubin said:
Do you anticipate having parameters like socket.AF_INET that are
currently integers, become enumeration members in future releases?

That, and the 're' flags referred to earlier, seems to be a good
application of enumerations.
 
B

Ben Finney

Roy Smith said:
A few random questions:

a = enum ('foo', 'bar', 'baz')
b = enum ('foo', 'bar', 'baz')

Two separate enumerations are created, and bound to the names 'a' and
'b'. Each one has three unique member values, distinct from any
others.
what's the value of the following:

a == b

False

(the enumeration objects are not the same, and don't contain the same
values)

False

(the enumeration objects are not the same)
a.foo == b.foo

False

(the comparison function returns NotImplemented when comparing values
from different enumerations. The Python interpreter [thanks Alex
Martelli :)] will evaluate the comparison as False)
a.foo is b.foo

False

(the members of each enumeration are created as separate values)

3

(enumerations are iterable)

Not defined in the current specification. Suggestions?

Not defined in the current specification. Suggestions?

-1210774164 # or some other hash value

(falls back on the 'object' hash function)

<type 'enum'> # if included in the language
Can you make an enum from a sequence?

The current specification is for an enum constructor to accept its
member names as separate arguments, to allow for the expected common
use case::

foo = enum('bar', 'baz', 'bucket')
syllables = ['foo', 'bar', 'baz']
c = enum (syllables)

Can be done with::

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.

That's what I intended; a sequence of arguments. Is there a better way
to refer to the positional arguments collectively?
BTW, I think this is a great proposal; enums are a badly needed part
of the language.

Thanks. I hope we can arrive at a consensus view of how enums should
work in Python.
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.

Yes, this is a prime use case for enums. I tried to cover this in the
"Motivation"::

Other examples include error status values and states
within a defined process.

Can anyone think of a better way to express this, without necessarily
referring to any specific set of flags or states or codes or whatever?
 
B

Ben Finney

Steven Bethard said:
-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 seems to be a common distinction.

Should I amend the PEP to propose "either in the builtins or in the
collections module"? Or should I propose two PEPs and let them
compete?
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.)

Replies to your post indicate this is another popular distinction.

But the terminology is broken. The term "enumerated" seems to me to
imply that it does have an order. Can you suggest a term other than
"enumerated" for what you're describing with the unordered property?
 
B

Ben Finney

Toby Dickenson said:
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.

True; I'll need to fix the specification so that it does say that.
s/arguments/strings/

s/arguments/string arguments/ :)
 
B

Ben Finney

Ben Finney said:
PEP: 354
Title: Enumerations in Python
Version: $Revision: 42186 $
Last-Modified: $Date: 2006-01-26 11:55:20 +1100 (Thu, 26 Jan 2006) $

Most people seem to be unopposed or in favour of some kind of
enumeration mechanism making it at least as far as the standard
library.

As I understand it, the current outstanding debates are::

* Builtin types versus stdlib module (maybe 'collections')

* Ordered sequences versus unordered iterables

* Immutable versus mutable

* Bad comparisons: raise exception versus return NotImplemented

* Terminology for each of these concepts

Any new issues before I make a revised draft?
 
P

Paul Rubin

Ben Finney said:
These don't seem simple or elegant. I don't see a particular benefit
to doing it that way, rather than being explicit about manipulating
the member list::

pentium_instructions = enum('add', 'sub', 'mul')
athlon64_instructions = enum(
*[ str(i) for i in pentium_instructions] + ['add64', 'sub64'] )

I don't know about this. It makes athlon64_instructions a completely
separate enum from pentium_instructions. It could be that
athlon64_instructions.add should be the same as pentium_instructions.add .
 
F

Felipe Almeida Lessa

Em Seg, 2006-02-27 às 17:10 -0800, Paul Rubin escreveu:
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

Or maybe just...

i386_instructions = enum('add', 'sub', 'mul', ...)
pentium_instructions = enum(i386_instructions, 'addf', 'subf',
'mulf', ...)
athlon64_instructions = enum(pentium_instructions, 'add64',
'sub64', ...)
myprocessor_instructions = enum('foo', 'bar', 'baz', ...)
all_instructions = enum(athlon64_instructions,
myprocessor_instructions)

....and it could infer from the type that it's another enum to be
included. Also...

(i386_instructions.add == pentium_instructions.add ==
athlon64_instructions.add == all_instructions.add) == True

....and so on.

--
"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"
 
R

Roy Smith

Ben Finney said:
Two separate enumerations are created

OK, most of the rest follows from that.
Not defined in the current specification. Suggestions?

Well, by analogy with
set([1, 2, 3])

I would think:

enum('foo', 'bar', 'baz')

would make sense.
Not defined in the current specification. Suggestions?

Hmm. Maybe what I suggested for str() would work for repr() too. I'm a
little worried, however, about things that aren't == but print the same.
It might make more sense for repr() to include the id (in the style of
-1210774164 # or some other hash value

I saw some debate about mutable or immutable. Doesn't making something
hashable kinda-sorta mean it has to be immutable?
That's what I intended; a sequence of arguments. Is there a better way
to refer to the positional arguments collectively?

I'm not really a language lawyer, so I can't say. I was mostly trying to
explore the corners of the envelope.
Yes, this is a prime use case for enums. I tried to cover this in the
"Motivation"::

Other examples include error status values and states
within a defined process.

Can anyone think of a better way to express this, without necessarily
referring to any specific set of flags or states or codes or whatever?

Cite the regex thread :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,193
Latest member
TopCryptoTaxSoftwares2024

Latest Threads

Top