new enum idiom

Alex Martelli aleaxit at yahoo.com
Sun Jan 7 16:29:23 EST 2001


"Jonathan Polley" <@collins.rockwell.com> wrote in message
news:3A58B9C0.9F2A5CC0 at collins.rockwell.com...
> Is is possible to modify the 'enum' function so that I don't have to
provide a
> class?

Sure -- the 'enum' function can define and return a local class, for
example.

> I would like to just do a:
>
> MIDI_Event = enum("NOTE_ON", "NOTE_OFF", 3, "POLYPHONIC_KEY_PRESSURE",
> "CONTROLLER_CHANGE")
>
> and then do MIDI_Event.NOTE_ON.

One approach might be:

def enum(*args):
    class Enum: pass
    curval = 0
    for arg in args:
        if type(arg)==type(''):
            setattr(Enum, arg, curval)
            curval += 1
        else:
            curval = arg
    return Enum

This assumes a non-string argument (typically an integer, but that's not
checked
here) is meant to set the value to be named by the string-argument
immediately
*following* it -- a substantial simplification.  So substantial, that if one
needed to
have non-string arguments signify the value of the *preceding* argument, it
might
be best to start by bringing about the simpler case first, i.e., right at
the start of
the function, something like:
    args = list(args)
    i = 0
    while i<len(args)-1:
        if type(args[i+1])!=type(''):
            args[i],args[i+1] = args[i+1],args[i]
            i += 1
        i += 1
to proceed as before.  Or maybe that would just be moving the complexity!-)

Needing to explicitly set values in an enum is rare enough, that it would
seem
fully acceptable to me to place the explicit values right _before_ their
names in
those rare cases, using the much-simpler code above.  Viva simplicity...!-)


> On a related note, how would I map between the enumeration and its ordinal
value?
> Would I add the methods 'ord' and 'val' to the base class
> (MIDI_Event.ord(MIDI_Event.NOTE_OFF)) yields 3 and MIDI_Event.val(0)
yields
> MIDI_Event.NOTE_ON)?

I don't get it.  MIDI_Event.NOTE_OFF denotes the value 3; why do you want to
have MIDI_Event.ord(x) return the same value x that it is passed as
argument?
If you have 'NOTE_OFF' as a string, you can use it as 2nd argument to
getattr,
with MIDI_Event as the 1st argument, etc.

If you _occasionally_ need to know what string (name) corresponds to a
non-string
value in the enumeration, a linear search may suffice; however, if
MIDI_Event is a
class, you cannot easily set up things so that MIDI_Event.nameOf(0) can be
called.

When functions are set as attributes of a class, they are mutated into
unbound
methods, which can be called only with a class-instance as the first
argument.  So,
MIDI_Event.nameOf would have to denote a callable that is not a function
(e.g.,
an instance of a class with a __call__ attribute).  Not easy or comfortable,
though
quite possible.

But just as you will use a non-method syntax such as
    getattr(MIDI_Event, 'NOTE_OFF')
to find the value corresponding to a name, why not use a similar syntax to
find
the name corresponding to a value?  THAT is easy...:

def getnameof(enum, value):
    for n, v in enum.__dict__.items():
        if v==value: return n
    else: raise ValueError, "value (%s) not found in enum (%s)" % (
            value,enum.__name__)

I would not bother to think up clever ways to use MIDI_Event.nameOf syntax
(although, actually, ensuring MIDI_Event is a class-*instance* object as
opposed to a class-*object* would be reasonably easy... and, on instances,
method-syntax IS easy to arrange!-).  Getting all that fancy in the pursuit
of a debatably-appropriate morsel of syntax sugar is a waste of energy!


Regarding performance, which IS a bit more important than syntax sugar...:

enums are typically small enough that a linear search should yield perfectly
acceptable performance, particularly considering that the value-to-name
mapping is hardly going to be a very frequent operation.

If, later, profiling your application should unexpectedly prove that the
getnameof performance is actually a bottleneck for it, it will be reasonably
easy to optimize it (by inserting a reverse-lookup dictionary as one of
the attributes of the Enum class if and when needed -- this will mean the
values have to be hashable, but, as they're meant to be typically integers,
that should be no serious problem).

However, don't do it unless the need is _proven_ (by profiling carefully,
if your application performance is not satisfactory).  *Do the simplest
thing that could possibly work*...


Alex






More information about the Python-list mailing list