PEP 354: Enumerations in Python

Steven D'Aprano steve at REMOVETHIScyber.com.au
Mon Feb 27 06:10:00 EST 2006


On Mon, 27 Feb 2006 00:43:45 -0800, Paul Rubin wrote:

> Steven D'Aprano <steve at 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
> 647574296340241173406516439806634217274336603815968998799147348150763731 ?

Of course. If you have a book with
647574296340241173406516439806634217274336603815968998799147348150763732
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.




More information about the Python-list mailing list