Bengt Richter said:
If [an enumeration has a fixed sequence], what is more natural
than using their index values as keys to other ordered info?
I don't see why. If you want sequence numbers, use enumerate(). If
not, the enum object itself can be used directly as an iterable.
I changed mine so the enum _class_ is iterable, but enum instances
are not.
I'm not really understanding your design.
In my enum package, an enumeration is an instance of Enum. Those
instances are iterable, producing EnumValue instances; the EnumValue
instances are also available as named attributes of the Enum instance.
I have a similar situation, except I have an enum factory function makeEnum
instead of a class Enum, and the factory returns not an instance of Enum,
but an enum as a unique type, i.e., a class. But it is functionally analogous,
except I differentiate by
... print str(val)
...
red
blue
green
That corresponds to
... print str(val),'-(repr)->', repr(val)
...
red -(repr)-> Colours.red
blue -(repr)-> Colours.blue
green -(repr)-> Colours.green
I added the repr to show that the vals were instances of something,
and they are actuall instances of the Colours type, which was uniquely
created to represent the enumeration, along with its fixed set of internally
cached immutable instances, references to which are returned by the Colours
constructor (__new__) instead of creating potentially duplicate instances.
So these instances can serve as sentinels too. The id's don't change, no
matter how many you construct (of a given member of the instance set). Of
course there are len(Colours) fixed (once created and cached) instances
of Colours in all.
>>> list(Colours) [Colours.red, Colours.blue, Colours.green]
>>> map(str, Colours) ['red', 'blue', 'green']
>>> map(int, Colours) [0, 1, 2]
>>> int(Colours.blue) 1
>>> Colours[1]
Colours.blue
Why would the Enum *class* be iterable?
That corresponds to asking why my makeEnum should be iterable,
and the it is not. What makeEnum makes functionally corresponds
to your Enum instance, except my "instance" is a unique manufactured class.
OTOH, the index values (and hence my enums) are[1] not very good
as unique dict keys, since they compare[2] promiscuously with
each other and other number types.
Indeed, that's why (in my design) the values from the enum are only
useful for comparing with each other. This, to me, seems to be the
purpose of an enumerated type.
Have you tried yet to use two different enum instances as keys in
the same dict?
What do you mean by "enum instances"? I presume you mean "values from
a single enum".
Yes, by analogy to your functionality, but since my "single enum" is
actually a class returned by makeEnum, not an instance of Enum, i.e.,
<class 'makeenum.Colours'>
and the "values" are immutable singleton instances that you can access via
the class (which I gave some unusual capabilities -- i.e., some methods
that apply to it as an ordered collection of immutable instances, and
which aren't accessible as methods of the instances (though the instances
have special methods of their own, and inherit methods from int).
... print colour_german[val]
...
rot
blau
grün
That's a plain dict with colour "values" as keys. Same as (checking the order first
to get the zip correspondence right ;-)
[Colours.red, Colours.blue, Colours.green]
... print 'colour_german[%r] => %s' %(val, colour_german[val])
...
colour_german[Colours.red] => rot
colour_german[Colours.blue] => blau
colour_german[Colours.green] => grün
Oh, perhaps you mean "enum values from different enum instances". No, Yes, again by analogy.
those won't compare against each other; there's no meaningful
relationship, since different enum instances are conceptually
different types.
Well, yes, I made that the strict default cmp, but I can optionally make
"Enum instances" (makeEnum returned classes) whose values cmp differently.
But now that I think of it, sorting is pretty controllable anyway, e.g,
suppose we had a Coins enum:
>>> Coins = makeEnum('Coins', 'penny nickel dime quarter')
>>> Colours[:] + Coins[:]
[Colours.red, Colours.blue, Colours.green, Coins.penny, Coins.nickel, Coins.dime, Coins.quarter]
>>> sorted(Colours[:] + Coins[:], key=repr)
[Coins.dime, Coins.nickel, Coins.penny, Coins.quarter, Colours.blue, Colours.green, Colours.red]
>>> sorted(Colours[:] + Coins[:], key=str)
[Colours.blue, Coins.dime, Colours.green, Coins.nickel, Coins.penny, Coins.quarter, Colours.red]
>>> map(str, sorted(Colours[:] + Coins[:], key=str)) ['blue', 'dime', 'green', 'nickel', 'penny', 'quarter', 'red']
>>> map(str, sorted(Colours[:] + Coins[:], key=int)) ['red', 'penny', 'blue', 'nickel', 'green', 'dime', 'quarter']
>>> map(str, sorted(Colours[:] + Coins[:], key=type)) ['red', 'blue', 'green', 'penny', 'nickel', 'dime', 'quarter']
>>> map(str, sorted(Colours[:] + Coins[:], key=lambda x
type(x).__name__, int(x)))) ['penny', 'nickel', 'dime', 'quarter', 'red', 'blue', 'green']
>>> sorted(Colours[:] + Coins[:], key=lambda x
type(x).__name__, int(x)))
[Coins.penny, Coins.nickel, Coins.dime, Coins.quarter, Colours.red, Colours.blue, Colours.green]
>>> sorted(Colours[:] + Coins[:], key=repr)
[Coins.dime, Coins.nickel, Coins.penny, Coins.quarter, Colours.blue, Colours.green, Colours.red]
I think this is a flaw, based on expecting too strong a relationship
between the enum value instance, and an integer value.
Actually, I'm not "expecting" it, I am defining it that way ;-)
Note the parallels between
>>> Bool = makeEnum('Bool', 'False True')
>>> issubclass(Bool, int) True
>>> issubclass(bool, int) True
>>> isinstance(Bool.False, int) True
>>> isinstance( False, int) True
>>> isinstance(Bool.True, int) True
>>> isinstance( True, int) True
>>> bf = bool(0)
>>> bf2 = bool(0)
>>> bf is bf2 True
>>> Bool
>>> Bf = Bool(0)
>>> Bf2 = Bool(0)
>>> Bf is Bf2 True
>>> Bf Bool.False
>>> Bf2 Bool.False
>>> bf False
>>> bf2 False
>>> map(int, [True, False]) [1, 0]
>>> map(int, [Bool.True, Bool.False])
[1, 0]
Why should colours.blue compare before fruits.orange? How is that
meaningful?
Like Finneys come before Richters in the telephone book ;-)
I'm still trying to understand what is served by having some exposed
relationship between an enum value instance and an integer value.
The relationship is effectively __int__ giving the index position in
the originally specified sequence of names, without having to do an index
operation, and without having to do anything but use a reference to
the value in a context where the integer value is useful.
It's no more necessary than saying that ["a", "b", "c"] requires
that there be some specific correlation between the values of that
list and the integers 0, 1, 2. If you *want* such a correlation, in
some particular case, use enumerate() to get it; but there's
nothing about the values themselves that requires that
correspondence.
That's true for arbitrary values in a list, but IMO it's not true for
values whose names were originally specified in a particular order for
(presumably -- or doesn't your enum care about the order??) a reason.
Enums in other languages have that default correspondence typically,
from what I can recall, and promotion to integer in arithmetic contexts
is also common. Having different enumerations be distinct _types_ is
exactly what C++ does. And Pascal.
Yet it is comforting to know that ['a', 'b', 'c'][0] will interpret
the [0] to mean the first in the sequence (unless someone is doing a
list-like repr trick based on some other type ;-).
Again, you're only talking about *sequence*, not correspondence to
integers. Your case above isn't an argument in favour of "the 'a'
value should coerce to the 0 value". Why then should an enum value
instance coerce to any particular integer value?
The particular integer values have no relation to their position in
an _arbitrary_ list, but they do have that relation to their position
in the originally specified sequence of their names.
Here you've lost me, since I don't understand why you want
enumerations to be classes at all. Are you going to be making
instances of an entire enumeration? If not, why make classes instead
of objects?
For the same reason C++ does. Different enumerations are logically
different types, not just different objects.
Somehow ISTM maybe you are using the enum name in an extended way that
really maybe calls for another name. Some kind of homogeneous frozen list
with names for the elements, which e.g. is more like struct than enum in C++.
What are the real use cases for your enums? And how are the same problems
usually solved? I guess I see the default case for C++ as a model, and
might extend that to a sparse sequence of integer values with names, but
without allowing instances of intervening values like C++. I don't know,
I have a feeling some experiments are just featuritis. Since sorts can
easily be controlled with a key function, maybe configuring with different
__cmp__ and __eq__ etc. is overkill. What's needed is some real use cases.
Maybe there is a place for both kinds of enums, if we give them different names ;-)
Regards,
Bengt Richter