By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
431,780 Members | 1,550 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 431,780 IT Pros & Developers. It's quick & easy.

Enumeration idioms: Values from different enumerations

P: n/a
[quoting private email with permission]

Antoon Pardon wrote:
I just downloaded your enum module for python [from the Cheeseshop]
and played a bit with it. IMO some of the behaviour makes it less
usefull.
Feedback is appreciated. I'm hoping to provide a "one obvious way" to
do enumerations in Python.
from enum import Enum
day = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
col = Enum('red', 'green', 'blue')
day.wed == col.blue Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "enum.py", line 100, in __cmp__
raise EnumValueCompareError(self, other)
enum.EnumValueCompareError: Not values from the same enumeration:
(EnumValue(<enum.Enum object at 0x4020c30c>, 2, 'wed'),
EnumValue(<enum.Enum object at 0x4021a14c>, 2, 'blue')) type(day.mon) == type(col.red) True type(day.mon) is type(col.red) True lst= [day.mon, col.red]
day.fri in lst As can be seen from the above, you raise an exception when one wants
to compare Enums from different enumarations, but it seems a bit
strange that different enumerations belong to the same type.
This does seem a point that could be confusing. Would it be better if
every Enum instance had its own unique subclass of EnumValue, that was
used to instantiate values for that enumeration?
I also think it would be more usefull if enums from different
enumerations just tested unequal.
My rationale for this is: The only purpose of an enumeration is to
have a set of arbitrary unique values within that enumeration. The
values make no sense in the context of a different enumeration, so I
see three different comparison results:
Weekday.mon == Weekday.mon True Weekday.fri == Weekday.mon False Colour.blue == Weekday.mon [...]
enum.EnumValueCompareError: Not values from the same enumeration

However, I'm aware that this is not how other Python types behave:
23 == 23 True 42 == 23 False "spam" == 23

False

Is there a semantic difference between these cases? Is that difference
enough to want different behaviour?

Is there some behaviour other than "evaluate to False" or "raise an
exception", that could indicate "not comparable"?
Because as it is you can't put elements from different enumerations
in a list and check with the in operator if a specific enum value is
in the list.

It could also cause problems for dictionaries, since keys in
dictionaries can be tested against a key giving in a subscription.


These are valid concerns. I can't see how to reconcile these against
the desire for values from different enums to fail comparison.

Am I alone in my tri-state view of enumeration value comparisons? Can
anyone else defend why values from different enumerations should not
compare?

--
\ "I was the kid next door's imaginary friend." -- Emo Philips |
`\ |
_o__) |
Ben Finney
Dec 16 '05 #1
Share this Question
Share on Google+
27 Replies


P: n/a
Ben Finney wrote:
These are valid concerns. I can't see how to reconcile these against
the desire for values from different enums to fail comparison.

Am I alone in my tri-state view of enumeration value comparisons? Can
anyone else defend why values from different enumerations should not
compare?


While I'm largely a disinterested bystander, it occurs to me that if you
look at the reasons behind wanting enumerations in the first place, you
might find a good reason to defend this view.

For example, if enumerations are intended to reduce the likelihood of
certain types of errors (where the use of typical XXXX=3 "constants"
might be more prone to errors), then perhaps this suggests that passing
errors silently is bad. That is, trying to compare enumerations that
should not be compared *is* an error (raising an exception) *because*
the whole point of enumerations is to avoid errors in such cases.

Or perhaps I'm off the mark as to why people want enumerations. Is it
just a cosmetic consideration? (Yes, I've used and appreciated them
extensively in C and might even pick up Enum for my Python code... I'm
just sort of playing devil's advocate here.)

-Peter

Dec 16 '05 #2

P: n/a
Peter Hansen <pe***@engcorp.com> writes:
For example, if enumerations are intended to reduce the likelihood of
certain types of errors (where the use of typical XXXX=3 "constants"
might be more prone to errors), then perhaps this suggests that
passing errors silently is bad. That is, trying to compare
enumerations that should not be compared *is* an error (raising an
exception) *because* the whole point of enumerations is to avoid
errors in such cases.


Except it might not be an error. For instance, if I've got a list of
enum objects taken from various types (say I've got one set of enums
for days of the week, another for months of the year, and so on, and
which I use depends on whether the user wants to select days of the
week, months of the year, etc), it makes perfect sense to want to know
if a specific enum value is in the list, and the obvious way to check
it is with "my_value in enum_list". That won't work if you raise an
exception - it takes a relatively convoluted bit of code to make this
test.

Python generally uses '==' to mean "is the same value". To do that, a
simple true/false return is enough. In raising an exception, you're
making '==' carry an extra meaning (I'm not sure *what* that is,
though). Do any python builtins behave that way? How about anything in
the python standard library?

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Dec 16 '05 #3

P: n/a
Op 2005-12-16, Ben Finney schreef <be************@benfinney.id.au>:
[quoting private email with permission]

Antoon Pardon wrote:
I just downloaded your enum module for python [from the Cheeseshop]
and played a bit with it. IMO some of the behaviour makes it less
usefull.


Feedback is appreciated. I'm hoping to provide a "one obvious way" to
do enumerations in Python.
>>> from enum import Enum
>>> day = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
>>> col = Enum('red', 'green', 'blue')
>>> day.wed == col.blue

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "enum.py", line 100, in __cmp__
raise EnumValueCompareError(self, other)
enum.EnumValueCompareError: Not values from the same enumeration:
(EnumValue(<enum.Enum object at 0x4020c30c>, 2, 'wed'),
EnumValue(<enum.Enum object at 0x4021a14c>, 2, 'blue'))
>>> type(day.mon) == type(col.red)

True
>>> type(day.mon) is type(col.red)

True
>>> lst= [day.mon, col.red]
>>> day.fri in lst
As can be seen from the above, you raise an exception when one wants
to compare Enums from different enumarations, but it seems a bit
strange that different enumerations belong to the same type.


This does seem a point that could be confusing. Would it be better if
every Enum instance had its own unique subclass of EnumValue, that was
used to instantiate values for that enumeration?


If you decide on keeping the current behaviour when comparing
values of different enumerations, I would definitely answer
yes.
I also think it would be more usefull if enums from different
enumerations just tested unequal.


My rationale for this is: The only purpose of an enumeration is to
have a set of arbitrary unique values within that enumeration. The
values make no sense in the context of a different enumeration, so I
see three different comparison results:
>>> Weekday.mon == Weekday.mon True >>> Weekday.fri == Weekday.mon False >>> Colour.blue == Weekday.mon [...]
enum.EnumValueCompareError: Not values from the same enumeration

However, I'm aware that this is not how other Python types behave:
>>> 23 == 23 True >>> 42 == 23 False >>> "spam" == 23

False

Is there a semantic difference between these cases? Is that difference
enough to want different behaviour?

Is there some behaviour other than "evaluate to False" or "raise an
exception", that could indicate "not comparable"?


This is a difficult question, because AFAIU python doesn't have clear
guidelines for this. I also have the impression that comparisons are
used for two different reasons.

The first is compare things in a mathematical kind of way in which
how things are compared is important.

The second is more an ordering mechanisme. Whether a string is greater
than an int or vice versa, is not imporant what is important is that
this order is consistent and can be used in binary searches, trees
and other structures to organise data.

Now python has only one collection of compare operators which can
cause conflicts. For instance, one could impose an order on sets
to use for such structure but that goes against the normal order
on sets which is based on subsets.

The only advise I have here is look at PEP 3000 that states:

Comparisons other than == and != between disparate types will raise
an exception unless explicitly supported by the type.

Which seem to imply that == and != should not raise an exception
but >, >=, <, <= should.

--
Antoon Pardon
Dec 16 '05 #4

P: n/a
Mike Meyer <mw*@mired.org> writes:
Peter Hansen <pe***@engcorp.com> writes:
That is, [perhaps] trying to compare enumerations that should not
be compared *is* an error (raising an exception) *because* the
whole point of enumerations is to avoid errors in such cases.
Except it might not be an error. For instance, if I've got a list of
enum objects taken from various types (say I've got one set of enums
for days of the week, another for months of the year, and so on, and
which I use depends on whether the user wants to select days of the
week, months of the year, etc), it makes perfect sense to want to know
if a specific enum value is in the list, and the obvious way to check
it is with "my_value in enum_list". That won't work if you raise an
exception - it takes a relatively convoluted bit of code to make this
test.


The 'enum' package in Cheeseshop doesn't do that. Enumerations are
sequences (of unique arbitrary values), that can be iterated and tested
for membership.

What's being discussed here is what happens when comparing the *values*
from the enumeration.
Python generally uses '==' to mean "is the same value". To do that,
a simple true/false return is enough. In raising an exception,
you're making '==' carry an extra meaning (I'm not sure *what* that
is, though).
The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
ShirtSize = Enum('small', 'medium', 'large')
AppleSize = Enum('small', 'large')
What should be the result of this comparison::
ShirtSize.small == AppleSize.small


Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).

If not, is 'False' the right way to indicate that?

Or is it an error to even try comparing them?
Do any python builtins behave that way? How about anything in the
python standard library?


No to both; I believe this may be a defining property of
enumerations. Am I wrong?

--
\ "Man cannot be uplifted; he must be seduced into virtue." -- |
`\ Donald Robert Perry Marquis |
_o__) |
Ben Finney
Dec 16 '05 #5

P: n/a
Op 2005-12-16, Ben Finney schreef <bi*****@polar.local>:
Mike Meyer <mw*@mired.org> writes:
Peter Hansen <pe***@engcorp.com> writes:
That is, [perhaps] trying to compare enumerations that should not
be compared *is* an error (raising an exception) *because* the
whole point of enumerations is to avoid errors in such cases.
Except it might not be an error. For instance, if I've got a list of
enum objects taken from various types (say I've got one set of enums
for days of the week, another for months of the year, and so on, and
which I use depends on whether the user wants to select days of the
week, months of the year, etc), it makes perfect sense to want to know
if a specific enum value is in the list, and the obvious way to check
it is with "my_value in enum_list". That won't work if you raise an
exception - it takes a relatively convoluted bit of code to make this
test.


The 'enum' package in Cheeseshop doesn't do that. Enumerations are
sequences (of unique arbitrary values), that can be iterated and tested
for membership.


Sure but we do have this:
from enum import Enum
day = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
col = Enum('red', 'green', 'blue')
lst= [day.mon, col.red]
col.blue in lst
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "enum.py", line 100, in __cmp__
raise EnumValueCompareError(self, other)
enum.EnumValueCompareError: Not values from the same enumeration:
(EnumValue(<enum.Enum object at 0x402181ac>, 2, 'blue'),
EnumValue(<enum.Enum object at 0x4020c36c>, 0, 'mon'))
What's being discussed here is what happens when comparing the *values*
from the enumeration.
Python generally uses '==' to mean "is the same value". To do that,
a simple true/false return is enough. In raising an exception,
you're making '==' carry an extra meaning (I'm not sure *what* that
is, though).


The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
>>> ShirtSize = Enum('small', 'medium', 'large')
>>> AppleSize = Enum('small', 'large')
What should be the result of this comparison::
>>> ShirtSize.small == AppleSize.small


Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).

If not, is 'False' the right way to indicate that?


I would agree 'False' is the right answer here.

--
Antoon Pardon
Dec 16 '05 #6

P: n/a
Ben Finney wrote:
The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
>>> ShirtSize = Enum('small', 'medium', 'large')
>>> AppleSize = Enum('small', 'large')
What should be the result of this comparison::
>>> ShirtSize.small == AppleSize.small
Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).


Is it possible to make it have the following sort of behaviour? :
ShirtSize.small == AppleSize.small True ShirtSize.small is AppleSize.small

False

It works for comparing a boolean (True) vs. an integer (1), so it has
some sort of precedent. (Especially if you make the tenuous assumption
that True,False are language-supported 'enums' for 0 and 1.)

--
Ben Sizer

Dec 16 '05 #7

P: n/a
Op 2005-12-16, Ben Sizer schreef <ky*****@gmail.com>:
Ben Finney wrote:
The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
>>> ShirtSize = Enum('small', 'medium', 'large')
>>> AppleSize = Enum('small', 'large')


What should be the result of this comparison::
>>> ShirtSize.small == AppleSize.small


Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).


Is it possible to make it have the following sort of behaviour? :
ShirtSize.small == AppleSize.small True ShirtSize.small is AppleSize.small

False

It works for comparing a boolean (True) vs. an integer (1), so it has
some sort of precedent. (Especially if you make the tenuous assumption
that True,False are language-supported 'enums' for 0 and 1.)


I'm against it. I don't like the following returning True:

ShirtSize.small in [ShirtSize.Medium, AppleSize.small]
I also think it may cause problems with other comparisons.

Supose the following:

col = Enum('red', 'green', 'blue')
paint = Enum('violet' , 'blue', 'red')

Then we get the following situation:

col.red == paint.red and col.blue == paint.blue

but

col.red < col.blue and paint.blue < paint.red

--
Antoon Pardon
Dec 16 '05 #8

P: n/a
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Ben Finney wrote:
The 'enum' package in Cheeseshop [defines enumerations as]
sequences (of unique arbitrary values), that can be iterated and
tested for membership.
Sure but we do have this:
from enum import Enum
day = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
col = Enum('red', 'green', 'blue')
lst= [day.mon, col.red]
col.blue in lst [...]
enum.EnumValueCompareError: Not values from the same enumeration:


Yes, that looks like a case in favour of allowing at least equal-value
and not-equal-value comparisons.
The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
>>> ShirtSize = Enum('small', 'medium', 'large')
>>> AppleSize = Enum('small', 'large')


What should be the result of this comparison::
>>> ShirtSize.small == AppleSize.small


Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).

If not, is 'False' the right way to indicate that?


I would agree 'False' is the right answer here.


Antoon Pardon wrote: Ben Finney wrote:
Would it be better if every Enum instance had its own unique
subclass of EnumValue, that was used to instantiate values for
that enumeration?
If you decide on keeping the current behaviour when comparing
values of different enumerations, I would definitely answer
yes.


If values from different enums were to be compared for equality (and
not raise an exception), would it still be good to have those values
be of different types?
This is a difficult question, because AFAIU python doesn't have
clear guidelines for [comparisons between differing types]. I also
have the impression that comparisons are used for two different
reasons. The first is compare things in a mathematical kind of way in which
how things are compared is important. The second is more an ordering mechanisme. Whether a string is
greater than an int or vice versa, is not imporant what is important
is that this order is consistent and can be used in binary searches,
trees and other structures to organise data. Now python has only one collection of compare operators which can
cause conflicts. For instance, one could impose an order on sets to
use for such structure but that goes against the normal order on
sets which is based on subsets. The only advise I have here is look at PEP 3000 that states: Comparisons other than == and != between disparate types will raise
an exception unless explicitly supported by the type. Which seem to imply that == and != should not raise an exception but
, >=, <, <= should.


This seems like a good guide for a policy on enumerations.

How about this proposal:

Enumerations (instances of Enum) contain a fixed sequence of unique,
arbitrary values. The values within an enumeration are instances of a
type (subclass of EnumValue), unique to that enumeration.

Values from an enumeration can be compared for equality (__eq__,
__ne__) with any other value, and will only return True for the same
value from the same enumeration, otherwise False. Other comparison
operations can be performed only with values from the same
enumeration, otherwise an exception (EnumValueCompareError, subclass
of ValueError) is raised.

This might result in the following simplistic implementation::

class EnumValue(object):
# ...

def __eq__(self, other):
result = False
if self is other:
result = True
return result

def __cmp__(self, other):
if isinstance(other, EnumValue):
if is_same_enumeration(self, other):
result = cmp(self.index, other.index)
else:
raise EnumValueCompareError(self, other)
return result

This gives meaning to the "equal value" comparisons, but ensures that
other comparisons are errors.

Comments so far?

--
\ "I like to fill my bathtub up with water, then turn the shower |
`\ on and pretend I'm in a submarine that's been hit." -- Steven |
_o__) Wright |
Ben Finney
Dec 16 '05 #9

P: n/a
Ben Finney <bi****************@benfinney.id.au> writes:
This gives meaning to the "equal value" comparisons, but ensures that
other comparisons are errors.

Comments so far?


What does copy.copy of an enumeration value do? What happens if you
have a list with some enumeration values inside, and you make a
deepcopy of it? What happens if you pickle an enum, then unpickle the
pickle and compare what comes out to the other enum?

All in all, comparing by object identity doesn't sound too good.
Maybe you want to have the enum contain its own name internally, and
do a string comparison.
Dec 16 '05 #10

P: n/a
> Antoon Pardon wrote:
Ben Finney wrote:
> Would it be better if every Enum instance had its own unique
> subclass of EnumValue, that was used to instantiate values for
> that enumeration?
If you decide on keeping the current behaviour when comparing
values of different enumerations, I would definitely answer
yes.


If values from different enums were to be compared for equality (and
not raise an exception), would it still be good to have those values
be of different types?


Yes, because people who would need a quick and dirty ordering
could then use the type of the object as a first key.
The only advise I have here is look at PEP 3000 that states:

Comparisons other than == and != between disparate types will raise
an exception unless explicitly supported by the type.

Which seem to imply that == and != should not raise an exception but
>, >=, <, <= should.


This seems like a good guide for a policy on enumerations.

How about this proposal:

Enumerations (instances of Enum) contain a fixed sequence of unique,
arbitrary values. The values within an enumeration are instances of a
type (subclass of EnumValue), unique to that enumeration.

Values from an enumeration can be compared for equality (__eq__,
__ne__) with any other value, and will only return True for the same
value from the same enumeration, otherwise False. Other comparison
operations can be performed only with values from the same
enumeration, otherwise an exception (EnumValueCompareError, subclass
of ValueError) is raised.


Looks good to me.
This might result in the following simplistic implementation::

class EnumValue(object):
# ...

def __eq__(self, other):
result = False
if self is other:
result = True
return result

def __cmp__(self, other):
if isinstance(other, EnumValue):
if is_same_enumeration(self, other):
result = cmp(self.index, other.index)
else:
raise EnumValueCompareError(self, other)
return result

This gives meaning to the "equal value" comparisons, but ensures that
other comparisons are errors.

Comments so far?


Isn't there an else missing? I have the impression that if you compare
two enums from a different enumeration __cmp__ will raise UnboundLocalError.

--
Antoon Pardon
Dec 16 '05 #11

P: n/a

Antoon Pardon wrote:
Op 2005-12-16, Ben Sizer schreef <ky*****@gmail.com>:
Is it possible to make it have the following sort of behaviour? :
> ShirtSize.small == AppleSize.small True
> ShirtSize.small is AppleSize.small

False

It works for comparing a boolean (True) vs. an integer (1), so it has
some sort of precedent. (Especially if you make the tenuous assumption
that True,False are language-supported 'enums' for 0 and 1.)


I'm against it. I don't like the following returning True:

ShirtSize.small in [ShirtSize.Medium, AppleSize.small]


I agree to an extent. I can see that being unwanted behaviour, although
not really a big one, and no worse than in C++. I think that when you
have a list of alternatives like that, they're either hard-coded by
selectively picking from the enumeration's initialisation list, or by
being generated according to some other criteria. Either way, it would
be hard to end up with the wrong type of value into that list, I think.
I also think it may cause problems with other comparisons.

Supose the following:

col = Enum('red', 'green', 'blue')
paint = Enum('violet' , 'blue', 'red')

Then we get the following situation:

col.red == paint.red and col.blue == paint.blue

but

col.red < col.blue and paint.blue < paint.red


I don't think that's a problem - does any other language make any
guarantees on ordering across multiple enumerations? Transitivity
within any single enumeration plus transivity of equivalence across
multiple enumerations, should be enough for most needs, no?

--
Ben Sizer

Dec 16 '05 #12

P: n/a

Without downloading and installing your code, can you tell me what the
result of these comparisons would be?

col = Enum('red', 'green', 'blue')
day = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
col.blue == "blue"
day.tue == 23

If they return False I would expect

col.blue == day.tue

to return False as well, not raise an exception.

Skip
Dec 16 '05 #13

P: n/a
ShirtSize = Enum('small', 'medium', 'large')
AppleSize = Enum('small', 'large')
Ben> What should be the result of this comparison::
ShirtSize.small == AppleSize.small

False. They are values from different objects. Just make __eq__ map to
"is". I think you'll be fine.

Ben> Or is it an error to even try comparing them?

As someone else pointed out containment tests become difficult with your
current formulation.
Do any python builtins behave that way? How about anything in the
python standard library?


Ben> No to both; I believe this may be a defining property of
Ben> enumerations. Am I wrong?

I think so. <0.5 wink>. I think you should be able to compare any two
objects. I think the complex types give you a little wiggle room on the
size comparisons (<, <=, >, >=), but I think == and != really ought to work.

Skip

Dec 16 '05 #14

P: n/a
Ben Finney <bi*****@polar.local> writes:
Mike Meyer <mw*@mired.org> writes:
Peter Hansen <pe***@engcorp.com> writes:
That is, [perhaps] trying to compare enumerations that should not
be compared *is* an error (raising an exception) *because* the
whole point of enumerations is to avoid errors in such cases. Except it might not be an error. For instance, if I've got a list of
enum objects taken from various types (say I've got one set of enums
for days of the week, another for months of the year, and so on, and
which I use depends on whether the user wants to select days of the
week, months of the year, etc), it makes perfect sense to want to know
if a specific enum value is in the list, and the obvious way to check
it is with "my_value in enum_list". That won't work if you raise an
exception - it takes a relatively convoluted bit of code to make this
test.

What's being discussed here is what happens when comparing the *values*
from the enumeration.


That's what I thought I was discussing, but apparently I wasn't clear
enough. Let me try again.

I think it's perfectly reasonable to store enum values from different
enums in a list, and check for a specific value being in that list. If
comparing two enum values can raise an exception, then doinng this
become problematic, as you may get an exception. According to what you
say below, this isn't true for any builtin type or any type in the
standard library.
Python generally uses '==' to mean "is the same value". To do that,
a simple true/false return is enough. In raising an exception,
you're making '==' carry an extra meaning (I'm not sure *what* that
is, though).

The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
>>> ShirtSize = Enum('small', 'medium', 'large')
>>> AppleSize = Enum('small', 'large')

Actually, it provides a very explicit guideline for these cases. The
guidelines is by no means obvious, as witness languages like LISP,
which have multiple equality operators.
What should be the result of this comparison:: >>> ShirtSize.small == AppleSize.small Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).


That depends on the semantics you want to place on the values of
enums. Reasonable arguments can be made for anything from "they're
exactly like small integers" to "the values are an implementation
detail, and you shouldn't worry about them." That they coerce to the
same string is irreleevant, at least in python, which doesn't coerce
strings to
Do any python builtins behave that way? How about anything in the
python standard library?

No to both; I believe this may be a defining property of
enumerations. Am I wrong?


There are enum implementations that don't treat comparing enum values
from different enums as an error, so I'd say you're wrong. In Python's
case, comparing two objects for identity never raises an exception for
any type that comes with the language, so I'd say that that was a
defining property of python.

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Dec 16 '05 #15

P: n/a
On Fri, 16 Dec 2005 15:16:08 +1100, Ben Finney wrote:
As can be seen from the above, you raise an exception when one wants
to compare Enums from different enumarations, but it seems a bit
strange that different enumerations belong to the same type.


This does seem a point that could be confusing. Would it be better if
every Enum instance had its own unique subclass of EnumValue, that was
used to instantiate values for that enumeration?


That seems to be creating complication for its own sake.

I also think it would be more usefull if enums from different
enumerations just tested unequal.


My rationale for this is: The only purpose of an enumeration is to
have a set of arbitrary unique values within that enumeration. The
values make no sense in the context of a different enumeration, so I
see three different comparison results:
>>> Weekday.mon == Weekday.mon True >>> Weekday.fri == Weekday.mon False >>> Colour.blue == Weekday.mon [...]
enum.EnumValueCompareError: Not values from the same enumeration

However, I'm aware that this is not how other Python types behave:
>>> 23 == 23 True >>> 42 == 23 False >>> "spam" == 23 False

Is there a semantic difference between these cases? Is that difference
enough to want different behaviour?


I don't believe so.

It seems reasonable to me for other comparisons such as __lt__ etc.
to fail unless the objects belong to the same enumeration. But equality
and inequality are special. Unordered objects support equality testing.

The question whether Mohammad is greater than or less than the mountain
has no answer, because we don't know how to order the two (by height? by
mass? by religious belief? by economic value? by importance to human
history?). But we can certainly answer the question is Mohammad equal to
the mountain -- the answer is certainly no, no matter how we order them.

Because as it is you can't put elements from different enumerations
in a list and check with the in operator if a specific enum value is
in the list.

It could also cause problems for dictionaries, since keys in
dictionaries can be tested against a key giving in a subscription.


These are valid concerns. I can't see how to reconcile these against
the desire for values from different enums to fail comparison.


Perhaps you are just being over-strict -- soon you'll be writing in Pascal
:-)

Failing order comparisons is reasonable, but why refuse to test for
equality? If you ask "is Monday equal to Blue?" the intuitive answer is
"no", not "my brain just crashed trying to evaluate the answer!".
--
Steven.

Dec 16 '05 #16

P: n/a
On Thu, 15 Dec 2005 23:53:36 -0500, Peter Hansen wrote:
Ben Finney wrote:
These are valid concerns. I can't see how to reconcile these against
the desire for values from different enums to fail comparison.

Am I alone in my tri-state view of enumeration value comparisons? Can
anyone else defend why values from different enumerations should not
compare?


While I'm largely a disinterested bystander, it occurs to me that if you
look at the reasons behind wanting enumerations in the first place, you
might find a good reason to defend this view.

For example, if enumerations are intended to reduce the likelihood of
certain types of errors (where the use of typical XXXX=3 "constants"
might be more prone to errors), then perhaps this suggests that passing
errors silently is bad. That is, trying to compare enumerations that
should not be compared *is* an error (raising an exception) *because*
the whole point of enumerations is to avoid errors in such cases.

In my opinion, rather than make all enums fail equality testing, two
better solutions would be:

(1) sub-class Enum to have StrictEnum. Enum allows equality testing for
normal Pythonic behaviour. StrictEnum fails equality testing for extra B&D
behaviour.

(2) Expect the user to make an explicit test when they want the stricter
testing. At the moment two different enumerations have the same class, but
Ben could (perhaps) hash the enumeration values to get a unique ID that
distinguishes one enumeration from another. Or some other strategy that
would allow something like:

if value.from_same_enumeration(other) and value == other

for value and other both enums.

Of the two, I suspect that (1) is far less work for both Ben and the
people using his class, although for introspection purposes it might be
nice to have some sort of unique enumeration ID. That could even be
something as simple as a tuple of the enumeration values.
--
Steven.

Dec 16 '05 #17

P: n/a
On Fri, 16 Dec 2005 19:45:51 +1100, Ben Finney wrote:
The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
>>> ShirtSize = Enum('small', 'medium', 'large')
>>> AppleSize = Enum('small', 'large')
What should be the result of this comparison::
>>> ShirtSize.small == AppleSize.small

Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).


I don't think anyone seriously intends to argue that ShirtSize.small
should test equal to AppleSize.small.
If not, is 'False' the right way to indicate that?
Why not? The intuitive answer is that they are "not equal".

In the case of apples and shirts, the intuitive answer can be misleading
because if you had a small enough shirt (say, for a Barbi doll) one might
be tempted to say "Yes, they are the same size".

A better example might be StringLength.long == BookSize.long. Both coerce
to the same string, but one measures physical length and the other
measures number of words. They are incompatible measures, and can't be
ordered. A strict physicist might argue that asking "is five kilograms
equal to three metres?" is a nonsensical question, but that's not the
Python way.

(And an even better physicist will answer that kilograms and metres can
not only be tested for equality, but can be ordered -- there are
mathematical transformations, common in relativistic physics, where all
physical quantities including time and mass, are transformed into lengths.
These physicists calculated *everything* in terms of dimensionless values
and multiples of length, length squared, length cubed, etc.)
Or is it an error to even try comparing them?


Perhaps there are unusual cases where comparing strings and ints for
equality should be an error. There may even be unusual cases where
comparing ints and floats should be an error. But I don't believe that
this should be the default behaviour, and it isn't the Python way.

Likewise for enums from different enumerations. By default they should
evaluate unequal, not raise an exemption.
--
Steven.

Dec 17 '05 #18

P: n/a
On Fri, 16 Dec 2005 02:43:35 -0800, Ben Sizer wrote:
Ben Finney wrote:
The problem with "is the same value" as an explanation for '==' is
that it doesn't help in cases such as::
>>> ShirtSize = Enum('small', 'medium', 'large')
>>> AppleSize = Enum('small', 'large')
What should be the result of this comparison::
>>> ShirtSize.small == AppleSize.small


Are they "the same value"? They're both "small" (and they both coerce
to the same string value, and in this case the same integer value).


Is it possible to make it have the following sort of behaviour? :
ShirtSize.small == AppleSize.small

True


Okay, so I was wrong to say that nobody was seriously suggesting that sort
of behaviour.
It works for comparing a boolean (True) vs. an integer (1), so it has
some sort of precedent. (Especially if you make the tenuous assumption
that True,False are language-supported 'enums' for 0 and 1.)


Enums are not conceptually subclasses of integers. Integers just happen to
be a useful method to implement enumerations.
--
Steven.

Dec 17 '05 #19

P: n/a
Paul Rubin <http://ph****@NOSPAM.invalid> writes:
All in all, comparing by object identity doesn't sound too good.
Maybe you want to have the enum contain its own name internally, and
do a string comparison.


The "__eq__ compares identity" was a glib pseudo-implementation; I
didn't think it through. The existing 'enum' implementation doesn't
use identity, and future ones won't either.

The intended meaning was more like:

class EnumValue(object):
# ...
def __eq__(self, other):
result = compare_attribute_values(self, other)
return result

In other words, identity doesn't matter, but every value from every
enumeration will compare inequal to each other. If an EnumValue
instance is copied, the copies will compare equal.

--
\ "Remember men, we're fighting for this woman's honour; which is |
`\ probably more than she ever did." -- Groucho Marx |
_o__) |
Ben Finney <http://www.benfinney.id.au/>
Dec 17 '05 #20

P: n/a
"Ben Sizer" <ky*****@gmail.com> writes:
Transitivity within any single enumeration plus transivity of
equivalence across multiple enumerations, should be enough for most
needs, no?


+1 to transitivity within an enumeration. -1 to transitivity across
enumerations. If they're supposed to be equivalent, why are you
separating them into different enumerations at all?

The whole point of an enumeration, as I see it, is to have a set of
values that are only meaningfully equal or sequenced within that
enumeration.

--
\ "You've got the brain of a four-year-old boy, and I'll bet he |
`\ was glad to get rid of it." -- Groucho Marx |
_o__) |
Ben Finney
Dec 17 '05 #21

P: n/a
Steven D'Aprano wrote:
On Fri, 16 Dec 2005 02:43:35 -0800, Ben Sizer wrote:
Is it possible to make it have the following sort of behaviour? :
> ShirtSize.small == AppleSize.small

True


Okay, so I was wrong to say that nobody was seriously suggesting that sort
of behaviour.


I was working under what looks to have been an incorrect assumption
that someone was looking for a way to do the above. After all, it was
mentioned in what seemed like seriousness. I can't think of a use case
for it, myself.
It works for comparing a boolean (True) vs. an integer (1), so it has
some sort of precedent. (Especially if you make the tenuous assumption
that True,False are language-supported 'enums' for 0 and 1.)


Enums are not conceptually subclasses of integers. Integers just happen to
be a useful method to implement enumerations.


Aren't they? They have discrete values, can be ordered and compared for
equality, etc. I think the 'numerate' part of 'enumeration' is a hint
here. They certainly don't look much further from being integers than
booleans do. But, if nobody actually needs the behaviour I described
before, this point is irrelevant.

--
Ben Sizer

Dec 19 '05 #22

P: n/a
On Mon, 19 Dec 2005 05:30:11 -0800, Ben Sizer wrote:
Enums are not conceptually subclasses of integers. Integers just happen to
be a useful method to implement enumerations.
Aren't they? They have discrete values, can be ordered and compared for
equality, etc.


Just like:

mammal, reptile, amphibian, bird, fish

They are discrete values, they can be ordered in many different ways
(alphabetically, by biological clade, by average number of genes, etc),
and compared for equality (fish is not bird). But they aren't numbers.

It is true that you can create a one-to-one mapping from integers to
enums, but then you can also create a one-to-one mapping from William
Shakespeare's sonnets to enums. That second implementation is,
unfortunately, rather impractical.

I think the 'numerate' part of 'enumeration' is a hint
here.
It certainly is a hint, but not of what you think. It means they are
countable, not that they are numbers. You could try looking "enumerate"
up in the dictionary: it means to count, as in "by the time she finished
enumerating all my flaws, I felt three inches high".
They certainly don't look much further from being integers than
booleans do.


You think?

hamburger, steak, fish, chicken, pizza

What meaning do you give to steak**fish? Should that meaning change if I
happened to have written pizza first instead of last?

The fact that they can be counted shouldn't fool you into thinking they
are numbers.

--
Steven.

Dec 19 '05 #23

P: n/a
Ben Finney wrote:
Antoon Pardon wrote:
I just downloaded your enum module for python [from the Cheeseshop]
and played a bit with it. IMO some of the behaviour makes it less
usefull.

[...]
I also think it would be more usefull if enums from different
enumerations just tested unequal.

[...]

However, I'm aware that this is not how other Python types behave:
>>> 23 == 23 True >>> 42 == 23 False >>> "spam" == 23 False

Is there a semantic difference between these cases? Is that difference
enough to want different behaviour?

Is there some behaviour other than "evaluate to False" or "raise an
exception", that could indicate "not comparable"?


Yes: return NotImplemented. Note that the == operator automagically
returns False in this case.
"spam".__eq__("spam") True "spam".__eq__("ham") False "spam".__eq__(23)

NotImplemented

This way, the user could explicitly call __eq__ and check for
NotImplemented if he desires the exceptional behavior.

- Eric

Dec 19 '05 #24

P: n/a
es****@gmail.com writes:
Ben Finney wrote:
Is there some behaviour other than "evaluate to False" or "raise an
exception", that could indicate "not comparable"?


Yes: return NotImplemented. Note that the == operator automagically
returns False in this case.
>>> "spam".__eq__("spam") True >>> "spam".__eq__("ham") False >>> "spam".__eq__(23)

NotImplemented

This way, the user could explicitly call __eq__ and check for
NotImplemented if he desires the exceptional behavior.


Thanks, I was unaware of the standard usage of NotImplemented. That
seems like a sensible solution.

<URL:http://docs.python.org/ref/types.html#l2h-29>

--
\ "My doctor told me to stop having intimate dinners for four. |
`\ Unless there are three other people." -- Orson Welles |
_o__) |
Ben Finney
Dec 19 '05 #25

P: n/a
On Tue, 20 Dec 2005 09:16:03 +1100, Ben Finney <bi****************@benfinney.id.au> wrote:
es****@gmail.com writes:
Ben Finney wrote:
Is there some behaviour other than "evaluate to False" or "raise an
exception", that could indicate "not comparable"?


Yes: return NotImplemented. Note that the == operator automagically
returns False in this case.
>>> "spam".__eq__("spam")

True
>>> "spam".__eq__("ham")

False
>>> "spam".__eq__(23)

NotImplemented

This way, the user could explicitly call __eq__ and check for
NotImplemented if he desires the exceptional behavior.


Thanks, I was unaware of the standard usage of NotImplemented. That
seems like a sensible solution.

<URL:http://docs.python.org/ref/types.html#l2h-29>

OTOH, that says the truth value is True, so this does not fail:
assert 'spam'.__eq__(23)

Which seems counterintuitive. The explicit value test
assert 'spam'.__eq__(23) is NotImplemented
would succeed irrespective of truth value,
but it seems contorted and unpythonic to have to write
assert 'spam'.__eq__(23) is False Traceback (most recent call last):
File "<stdin>", line 1, in ?
AssertionError
to get the effect you might naively expect from
assert 'spam'.__eq__(23)

I wonder what the rationale for the NotImplemented True truth value was, as opposed to False.

On the third hand, I recently posted an idea of using NotImplemented
as a (handy because predefined) logically true sentinel value to return
from a test function to distinguish not_applicable from applicable_and_ok
when desired, yet allowing assert testfun(...) to succeed either way.
That code would have to use another boolishly True sentinel if NotImplemented
were changed to have False truth value.

Maybe it would be useful to have a couple of builtin sentinels like TrueSentinel
and FalseSentinel (or sentinel_t and sentinel_f or pick some names ;-) so we
wouldn't be tempted to misuse unique builtins like NotImplemented and Exception etc.

Regards,
Bengt Richter
Dec 20 '05 #26

P: n/a
Max
Steven D'Aprano wrote:
They certainly don't look much further from being integers than
booleans do.

You think?

hamburger, steak, fish, chicken, pizza

What meaning do you give to steak**fish? Should that meaning change if I
happened to have written pizza first instead of last?


What meaning do you give to True**False?

The fact that they can be counted shouldn't fool you into thinking they
are numbers.


--Max
Dec 23 '05 #27

P: n/a
On Fri, 23 Dec 2005 23:51:22 +0200, Max wrote:
Steven D'Aprano wrote:
They certainly don't look much further from being integers than
booleans do.

You think?

hamburger, steak, fish, chicken, pizza

What meaning do you give to steak**fish? Should that meaning change if I
happened to have written pizza first instead of last?


What meaning do you give to True**False?


Given that True == 1 and False == 0 in Python (which in my opinion is
unfortunate), then it is obviously 1.

True booleans don't support arithmetic operators like **, and neither
should enums.

--
Steven.

Dec 24 '05 #28

This discussion thread is closed

Replies have been disabled for this discussion.