Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

new enum idiom

8 views
Skip to first unread message

Will Ware

unread,
Jan 6, 2001, 10:41:58 AM1/6/01
to
I stumbled upon a new idiom for enums (at least it's new to me) that
some folks might find helpful. The standard idiom is of course:

(FIRST, SECOND, THIRD) = range(3)

and I would have used this except that I had too many values to count
conveniently. Also I wanted to keep around string versions of the enum
names, for __repr__ purposes. So I hit upon this idiom:

class MidiEvent:

typeNames = [
"NOTE_ON", "NOTE_OFF", "POLYPHONIC_KEY_PRESSURE", "CONTROLLER_CHANGE",
# dozens and dozens more...
]

# build an enum, this is done once when the module is imported
# locals() gives the namespace for this class, so they become
# class attributes

i = 0
for t in typeNames:
locals()[t] = i
i = i + 1

def __repr__(self):
# here is where the string versions are used
r = ("<MidiEvent %s blah blah blah" %
(self.typeNames[self.type], blah, blah, blah))
# other info
return r + ">"
--
import string,time,os;print string.join((lambda x:x[:10]+x[8:])(map(
lambda x:string.center("*"*(lambda x:((x<24) ### Seasons Greetings, Will Ware
*(x-3))+3)(x),24),range(1,28, 2))),"\n") ################ ww...@world.std.com

Carel Fellinger

unread,
Jan 6, 2001, 9:18:57 PM1/6/01
to
Will Ware <ww...@world.std.com> wrote:
...

> and I would have used this except that I had too many values to count
> conveniently. Also I wanted to keep around string versions of the enum
> names, for __repr__ purposes. So I hit upon this idiom:

> class MidiEvent:

> typeNames = [
> "NOTE_ON", "NOTE_OFF", "POLYPHONIC_KEY_PRESSURE", "CONTROLLER_CHANGE",
> # dozens and dozens more...
> ]

> # build an enum, this is done once when the module is imported
> # locals() gives the namespace for this class, so they become
> # class attributes

According to the docs locals() returns dicts that are ment to be
read-only:( So you'll have to spell it out. Unfortunately __dict__ is
unnown at this moment, so is MidiEvent. Simply postpone it till after
the class definition of MidiEvent is finished.

Classs MidiEvent:
typeNames = ["NOTE_ON", "NOTE_OFF", "...

i = 0
for t in MidiEvent.typeNames:
MidiEvent.__dict__[t] = i

And inspired by Alex's recent contribution for parsing possibly
`valued' command-line like options you could easily enhance this
enumeration idiom to deal with things as enum (a, b=2, c, d=4).


def enum(C, *args, **keys):
'''Create and enumerate to your hearts delight,
C is the class that wants enumerations,
*args is a sequence of enumeration names, possibly interleaved
with values that override the default value by specifying
the value for the directly preceding enum name.
**keys is a hack to allow to override the default name (enumRepr)
for the dict used to store the reverse mapping from enum
value to enum string
'''
i, enumRepr = -1, {} # i is the value of the last added enum
for x in args:
if type(x) == type(""):
i += 1
C.__dict__[x], enumRepr[i] = i, x
elif x>i:
C.__dict__[enumRepr[i]], enumRepr[x] = x, enumRepr[i]
del enumRepr[i]
i = x
elif x<i:
raise ValueError, "Enumeration value out of bound"
C.__dict__[keys.get(enumRepr, "enumRepr")] = enumRepr


Class MidiEvent:

def __repr__(self):


r = ("<MidiEvent %s blah blah blah" %

(self.enumRepr[self.type], blah, blah, blah))


# other info
return r + ">"

enum(MidiEvent,
"NOTE_ON", "NOTE_OFF", 3, "POLYPHONIC_KEY_PRESSURE", "CONTROLLER_CHANGE")


> --
> import string,time,os;print string.join((lambda x:x[:10]+x[8:])(map(
> lambda x:string.center("*"*(lambda x:((x<24) ### Seasons Greetings, Will Ware
> *(x-3))+3)(x),24),range(1,28, 2))),"\n") ################ ww...@world.std.com

And the best wishes to you too. (you know that in Holland the sixth
of jan. is taken to be the day that your christmas tree has to vanish:)
--
groetjes, carel

Emile van Sebille

unread,
Jan 6, 2001, 10:23:47 PM1/6/01
to

"Carel Fellinger" <cfel...@iae.nl> wrote in message
news:938jmh$919$1...@animus.fel.iae.nl...
<snip>

> And the best wishes to you too. (you know that in Holland
the sixth
> of jan. is taken to be the day that your christmas tree
has to vanish:)
> --
> groetjes, carel

But is the Queen's birthday still a verpligte snipperdag?


hoping-I-spelt-that-right-ly y'r

--

Emile van Sebille
em...@fenx.com
-------------------


Jonathan Polley

unread,
Jan 7, 2001, 1:47:29 PM1/7/01
to
Is is possible to modify the 'enum' function so that I don't have to provide a
class? I would like to use something like this method to create enumerated types,
but I don't want to keep generating classes that say:

class Ufda:
def __repr__(self):
return self.Enum_Repr[self.type]

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.

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)?

Not being very use to OO and classes, I may need some adult supervision on this ;)

Thanks,

Jonathan Polley
jwpo...@collins.rockwell.com

Jan Dries

unread,
Jan 7, 2001, 4:09:22 PM1/7/01
to pytho...@python.org

Jonathan Polley wrote:
>
> Is is possible to modify the 'enum' function so that I don't have to provide a
> class? I would like to use something like this method to create enumerated types,
> but I don't want to keep generating classes that say:
>

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

I once wrote a class to simulate enums. Using it, you can write:

colors = enum("GREEN","BLUE","RED","YELLOW")

and you can subsequently refer to the enum constants as:

x = colors.GREEN

If no value is supplied, the first name is assigned 0, the second 1 and
so on. But you can supply values, as in:

colors = enum("GREEN=1","BLUE=0x08","RED","YELLOW")

Anyway, here's the class:

import re

class enum:
def __init__(self,*names):
self.__mnames = {}
value = 0;
for i in names:
pairs = re.split("\s*[=]\s*",i)
if len(pairs) == 2:
value = eval(pairs[1])
self.__mnames[pairs[0]] = value
value += 1

def __getattr__(self,name):
try:
return self.__mnames[name]
except:
raise AttributeError

def __setattr__(self,name,value):
if name == "_enum__mnames":
self.__dict__[name] = value
else:
raise AttributeError

Jonathan Polley

unread,
Jan 7, 2001, 3:23:07 PM1/7/01
to
Forget about the ordinal/value conversion, I keep thinking in terms of strongly typed
languages (sigh). I have also modified the enum function so that it no longer needs to
be passed a class, but instantiates one of its own and returns it to the caller.

>>> MIDI_Event = enum.enum ("NOTE_ON", "NOTE_OFF", 3, "POLYPHONIC_KEY_PRESSURE",
"CONTROLLER_CHANGE")
>>> MIDI_Event.NOTE_ON
0
>>> MIDI_Event.POLYPHONIC_KEY_PRESSURE
4


Jonathan Polley
jwpo...@collins.rockwell.com


# module begins

import types

# a private class that will be instantiated for each enumeration
class __Enum_Class:
# an empty constructor
def __init__(self):
pass
# print out the name of the base class, FWIW
def __repr__(self):
return `self.__class__`

def enum(*args, **keys):


"""Create and enumerate to your hearts delight,

args is a sequence of enumeration names, possibly interleaved
with values that override the default value by specifying
the value for the directly preceding enum name.

**keys is a hack to allow to override the default name (Enum_Repr)


for the dict used to store the reverse mapping from enum
value to enum string"""

# The value of the last added enum
Last_Enum = -1

# the dictionary mapping enumerations to values
Enum_Repr = {}

# an instance of an enumerated type container
New_Enum = __Enum_Class()

# for each argument
for Enum in args:
# if I was passed a string enumeration
if type(Enum) == types.StringType:
# add one element to the enumeration
Last_Enum += 1
# add the value to the enumerated list
New_Enum.__dict__[Enum] = Last_Enum
Enum_Repr[Last_Enum] = Enum

# if not a string, it had better be an integer!
elif type (Enum) != types.IntType:
raise ValueError, "Enumeration must be a string or integer"

# check for a change in the enumeration sequence
elif Enum > Last_Enum:
# change the ordinal value of the last enumeration
New_Enum.__dict__[Enum_Repr[Last_Enum]] = Enum
# move the enumeration to its new position
Enum_Repr[Enum] = Enum_Repr[Last_Enum]
# delete the old position of the enumeration
del Enum_Repr[Last_Enum]
# remember the new value of the end of the enumeration
Last_Enum = Enum

# check if the enumeration change was backward!
elif Enum < Last_Enum:


raise ValueError, "Enumeration value out of bound"

# end if I was passed a string enumeration
# end for each argument

# store the enumeration string in the class' dictionary
New_Enum.__dict__[keys.get(Enum_Repr, "__Enum_Repr")] = Enum_Repr

# return the new enumeration to the caller
return New_Enum

# end def enum

Alex Martelli

unread,
Jan 7, 2001, 4:29:23 PM1/7/01
to
"Jonathan Polley" <@collins.rockwell.com> wrote in message
news:3A58B9C0...@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

Carel Fellinger

unread,
Jan 7, 2001, 7:58:23 PM1/7/01
to
Emile van Sebille <em...@fenx.com> wrote:

> But is the Queen's birthday still a verpligte snipperdag?

Yes, we even fixed this birthday-date for all future queens to
be that of our current queen-mother, the last one we all loved.

> hoping-I-spelt-that-right-ly y'r

I think that's the way they spell it in South Africa, here we
use ch instead of g, but don't worry it sound the same, so it
is equally difficult to pronounce for non natives:)
--
groetjes, carel

Carel Fellinger

unread,
Jan 8, 2001, 5:01:28 PM1/8/01
to
Alex Martelli <ale...@yahoo.com> wrote:
...

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

> 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...!-)

Simplicity of implementation, yes. But is this what one would expect?
I think all the languages I know that provide an enumaration method
that allows for changing the count midway spell it the other way
around, first the name then the value. So I strongly argue that in
this particular case you better invest a little more in the coding of
this enum class to prevent all those easy slip of the keys in the use
of it, especially since its use is so rare. Arguely an even better
approach would be to have such new counting points be spelled as
tuples; a little more eye-catching and simplifying the code even
further:)


def enum(*args):
class Enum: pass
curval = 0
for arg in args:

if type(arg)==type(()):
arg, curval = arg


setattr(Enum, arg, curval)
curval += 1

return Enum


MidiEvent = enum("NOTE_ON", ("NOTE_OFF", 2), "POLYPHONIC_KEY_PRESSURE"


# dozens and dozens more...

)

--
groetjes, carel

Alex Martelli

unread,
Jan 9, 2001, 8:27:44 AM1/9/01
to
"Carel Fellinger" <cfel...@iae.nl> wrote in message
news:93dde1$8d2$1...@animus.fel.iae.nl...
[snip]

> > 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
> ...
> > 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...!-)
>
> Simplicity of implementation, yes. But is this what one would expect?

Personally, I would expect _nothing in particular_ when reading
something like

xx = enum('foo', 'bar', 32, 'fee', 'fie', 45, 'flip', 'flop')

i.e., just a list of name-like strings occasionally mixed with
values that clearly aren't names.


> I think all the languages I know that provide an enumaration method
> that allows for changing the count midway spell it the other way
> around, first the name then the value.

With some extra syntax, such as equal-signs, or arrowlike squigglies
pointing in either explicit direction, expectations would surely firm
up. E.g., if I read:

xx = enum('foo', 'bar', 32, '<-fee', 'fie', 45, '<-flip', 'flop')

I would guess the arrows are suggesting 32 is the value of 'fee' and
45 the value of 'flip, while if I read:

xx = enum('foo', 'bar->', 32, 'fee', 'fie->', 45, 'flip', 'flop')

then the different arrows would suggest 32 as the value of 'bar' and
45 as that of 'fie'.


Absent such extra syntax clues, though, I don't see expectations
being meaningful either way. What language lets you define enums
with _some_ values out-of-order by just letting you list names
and non-name values with no further demarcation?


Python's mantra "when in doubt, avoid the temptation to guess" would
then *strongly* urge one to drop this complication altogether.


> of it, especially since its use is so rare. Arguely an even better
> approach would be to have such new counting points be spelled as
> tuples; a little more eye-catching and simplifying the code even
> further:)

That's one possibility. Syntax such as "bar=32" (for names in the
enumlist whose value is explicitly defined) being another alternative,
still better than 'just the list' (and a split on that form is
enough to make it into the name/value pair, so it would be easy
enough to accept either).


Alex

Robert Amesz

unread,
Jan 10, 2001, 8:10:49 AM1/10/01
to
Alex Martelli wrote:

>[Enums]


>
>With some extra syntax, such as equal-signs, or arrowlike squigglies
>pointing in either explicit direction, expectations would surely
>firm up. E.g., if I read:
>
> xx = enum('foo', 'bar', 32, '<-fee', 'fie', 45, '<-flip',
> 'flop')
>
>I would guess the arrows are suggesting 32 is the value of 'fee' and
>45 the value of 'flip, while if I read:
>
> xx = enum('foo', 'bar->', 32, 'fee', 'fie->', 45, 'flip',
> 'flop')
>
>then the different arrows would suggest 32 as the value of 'bar' and
>45 as that of 'fie'.
>

> [Snip]


>
>That's one possibility. Syntax such as "bar=32" (for names in the
>enumlist whose value is explicitly defined) being another
>alternative, still better than 'just the list' (and a split on that
>form is enough to make it into the name/value pair, so it would be
>easy enough to accept either).


Either way seems rather ugly to me. Why not use tuples to visually
suggest the link between the value and the string? In this case that
would make:

xx = enum('foo', ('bar',32), 'fee', ('fie',45), 'flip', 'flop')

It also would be much easier to parse the argument list in that form.


Robert Amesz

Alex Martelli

unread,
Jan 10, 2001, 12:12:01 PM1/10/01
to
"Robert Amesz" <rcameszR...@dds.removethistoo.nl> wrote in message
news:90259DC4...@127.0.0.1...
[snip]

> >That's one possibility. Syntax such as "bar=32" (for names in the
> >enumlist whose value is explicitly defined) being another
[snip]

> Either way seems rather ugly to me. Why not use tuples to visually
> suggest the link between the value and the string? In this case that
> would make:
>
> xx = enum('foo', ('bar',32), 'fee', ('fie',45), 'flip', 'flop')
>
> It also would be much easier to parse the argument list in that form.

Fine, but can't be "*much* easier" -- it's just one .split away from
the 'bar=32' proposal to ('bar','32'), after all:-).


Alex

Rainer Deyke

unread,
Jan 10, 2001, 5:45:31 PM1/10/01
to
"Alex Martelli" <ale...@yahoo.com> wrote in message
news:93i57...@news2.newsguy.com...

The problem with 'bar=32' is that it cannot be interleaved with unnumbered
args:

>>> enum('foo', bar=32, 'fee')
SyntaxError: non-keyword arg after keyword arg

Even if it could, there would be no way to determine the order of the
arguments and therefore no way to find out that 'fee' should have a value of
33.


--
Rainer Deyke (ro...@rainerdeyke.com)
Shareware computer games - http://rainerdeyke.com
"In ihren Reihen zu stehen heisst unter Feinden zu kaempfen" - Abigor


Steve Holden

unread,
Jan 10, 2001, 9:20:20 PM1/10/01
to
"Rainer Deyke" <ro...@rainerdeyke.com> wrote in message
news:fC576.21284$ge4.9...@news2.rdc2.tx.home.com...

> "Alex Martelli" <ale...@yahoo.com> wrote in message
> news:93i57...@news2.newsguy.com...
[ etc ]

> > Fine, but can't be "*much* easier" -- it's just one .split away from
> > the 'bar=32' proposal to ('bar','32'), after all:-).
>
> The problem with 'bar=32' is that it cannot be interleaved with unnumbered
> args:
>
> >>> enum('foo', bar=32, 'fee')
> SyntaxError: non-keyword arg after keyword arg
>
> Even if it could, there would be no way to determine the order of the
> arguments and therefore no way to find out that 'fee' should have a value
of
> 33.
>
This seems a little obtuse. What would be wrong with

enum('foo', 'bar=32', 'fee')

regards
Steve


Rainer Deyke

unread,
Jan 10, 2001, 11:36:25 PM1/10/01
to
"Steve Holden" <sho...@holdenweb.com> wrote in message
news:8N876.2727$24.3...@e420r-atl2.usenetserver.com...

> "Rainer Deyke" <ro...@rainerdeyke.com> wrote in message
> > >>> enum('foo', bar=32, 'fee')
> > SyntaxError: non-keyword arg after keyword arg

> This seems a little obtuse. What would be wrong with


>
> enum('foo', 'bar=32', 'fee')

Oh, right. That would work, but I don't like it. Parsed string arguments
lead to the dark side. It's only a small step to this:

enum('foo, bar=32, fee')

When that happens, we are no longer using the same language.

Alex Martelli

unread,
Jan 11, 2001, 4:52:34 AM1/11/01
to
"Rainer Deyke" <ro...@rainerdeyke.com> wrote in message
news:fC576.21284$ge4.9...@news2.rdc2.tx.home.com...
[snip]

> > > xx = enum('foo', ('bar',32), 'fee', ('fie',45), 'flip', 'flop')
> > >
> > > It also would be much easier to parse the argument list in that form.
> >
> > Fine, but can't be "*much* easier" -- it's just one .split away from
> > the 'bar=32' proposal to ('bar','32'), after all:-).
>
> The problem with 'bar=32' is that it cannot be interleaved with unnumbered
> args:
>
> >>> enum('foo', bar=32, 'fee')
> SyntaxError: non-keyword arg after keyword arg

Quotes vs metaquotes confusion -- I did mean 'bar=32', complete
with quotes (which is why I mentioned .split), not without (as
a keyword-argument).


Alex

Robert Amesz

unread,
Jan 11, 2001, 7:38:51 AM1/11/01
to
Alex Martelli wrote:

>"Robert Amesz" <rcameszR...@dds.removethistoo.nl> wrote in
>message news:90259DC4...@127.0.0.1...
>>

>> [Snip]


>>
>> It also would be much easier to parse the argument list in that
>> form.
>
>Fine, but can't be "*much* easier" -- it's just one .split away from
>the 'bar=32' proposal to ('bar','32'), after all:-).

Oh, allright. let's just call it a *little* easier, then. I mainly like
the lispy flavour. Also, as a small extra bonus, you can use
expressions with the tuple-notation. That allows you to do things like:

#global constants
base1 = 100
base2 = 200

#Someplace else
xx = enum(('foo',base1), 'foo2', 'foo3', ('bar',base2), 'etc')


Of course, you can always eval() the right hand part of 'foo=base1',
but the other form catches any syntax errors in those expressions
before the program actually runs.


Robert Amesz

Alex Martelli

unread,
Jan 11, 2001, 7:52:44 AM1/11/01
to
"Robert Amesz" <rcameszR...@dds.removethistoo.nl> wrote in message
news:90268BCE...@127.0.0.1...
[snip]

> Oh, allright. let's just call it a *little* easier, then. I mainly like
> the lispy flavour.

Which is probably what will turn many other people OFF:-).

> Also, as a small extra bonus, you can use
> expressions with the tuple-notation. That allows you to do things like:
>
> #global constants
> base1 = 100
> base2 = 200
>
> #Someplace else
> xx = enum(('foo',base1), 'foo2', 'foo3', ('bar',base2), 'etc')
>
> Of course, you can always eval() the right hand part of 'foo=base1',
> but the other form catches any syntax errors in those expressions
> before the program actually runs.

But the string-syntax goes one better by allowing, not just syntax
checking (at compile time), but also type-checking (at runtime) if
you wish to specify it:

xx = enum('foo=%s'%base1, 'foo2', 'foo3', 'bar=%d'%base2, 'etc')

here, base1 is accepted whatever type it is (as long as it's able
to produce a string-form:-), but base2 is requested to be able to
produce an integer specifically.

Very un-Lispish, no doubt, but, IS this comp.lang.lisp...?-)


Alex

Robert Amesz

unread,
Jan 11, 2001, 7:33:56 PM1/11/01
to
Alex Martelli wrote:

>"Robert Amesz" <rcameszR...@dds.removethistoo.nl> wrote in
>message news:90268BCE...@127.0.0.1...
> [snip]
>> Oh, allright. let's just call it a *little* easier, then. I mainly
>> like the lispy flavour.
>
>Which is probably what will turn many other people OFF:-).

Ah, Lisp, so beautiful, so pristine, so ... verbose. Well, it's great
for lists, but anything numeric is a chore. Let's see:
(EQ (PLUS 1 1) 2)
t
Phew, so least that's still true... ;-)

Still, Lisp must have influenced Python, there's the 'lambda' and 'map'
to prove it.


>> Also, as a small extra bonus, you can use
>> expressions with the tuple-notation. That allows you to do things
>> like:
>>
>> #global constants
>> base1 = 100
>> base2 = 200
>>
>> #Someplace else
>> xx = enum(('foo',base1), 'foo2', 'foo3', ('bar',base2), 'etc')
>>
>> Of course, you can always eval() the right hand part of
>> 'foo=base1', but the other form catches any syntax errors in those
>> expressions before the program actually runs.
>
>But the string-syntax goes one better by allowing, not just syntax
>checking (at compile time), but also type-checking (at runtime) if
>you wish to specify it:
>
> xx = enum('foo=%s'%base1, 'foo2', 'foo3', 'bar=%d'%base2, 'etc')
>
>here, base1 is accepted whatever type it is (as long as it's able
>to produce a string-form:-), but base2 is requested to be able to
>produce an integer specifically.

Hmm I see a problem here: doesn't the %-operator take a tuple on the
right hand side, or has that changed in 2.0? In 1.52 it should be:

xx = enum('foo=%s'%(base1,), 'foo2', 'foo3', 'bar=%d'%(base2,),
'etc')

That doesn't make it any prettier.

Anyhow it's not hard to coerce something into an int, so the entire
'bar=%d' vs. 'bar=%s' argument doesn't really matter. I *do* agree that
any arguments must be typechecked explicitly, that's just sound
programming practice.


As both forms seem equally capable to get the job done, and I can't see
the one method being significantly more efficient that the other, in
the end it would an argument over aesthetics.

Aesthetic argument 1: I don't like data making an unneccesary roundtrip
into a different domain. When using 's=n' numeric data (n) has to be
expressed as a string.

Aesthetic argument 2: The form 's=n' is in fact a tuple coded as a
string, so why not get rid of the coding and make it explicit? After
all, one of Pythons design paradigms would to be: "explicit is better
than implicit".


>Very un-Lispish, no doubt, but, IS this comp.lang.lisp...?-)

Oddly enough, the thing which attracted me to Python was the C-meets-
Lisp quality of its syntax. Both C and Lisp are - in my opnion - among
the purest and most minimalist types of languages. Python seems to be
able to combine the two extreme paradigms into an effective, yet clean
language.

So there you have it, it's out in the open now:

I'm Robert Amesz
-- and I'm a Python-user... ;-)

Fredrik Lundh

unread,
Jan 12, 2001, 3:17:35 AM1/12/01
to
Robert Amesz wrote:
> Hmm I see a problem here: doesn't the %-operator take a tuple on the
> right hand side, or has that changed in 2.0?

no, and no.

http://www.python.org/doc/current/lib/typesseq-strings.html

"The right argument should be a tuple with one item
for each argument required by the format string; if the
string requires a single argument, the right argument
may also be a single non-tuple object"

Cheers /F


Alex Martelli

unread,
Jan 12, 2001, 5:12:12 AM1/12/01
to
"Robert Amesz" <rcameszR...@dds.removethistoo.nl> wrote in message
news:90273B4...@127.0.0.1...
[snip]

> > xx = enum('foo=%s'%base1, 'foo2', 'foo3', 'bar=%d'%base2, 'etc')
> >
> >here, base1 is accepted whatever type it is (as long as it's able
> >to produce a string-form:-), but base2 is requested to be able to
> >produce an integer specifically.
>
> Hmm I see a problem here: doesn't the %-operator take a tuple on the
> right hand side, or has that changed in 2.0? In 1.52 it should be:
>
> xx = enum('foo=%s'%(base1,), 'foo2', 'foo3', 'bar=%d'%(base2,),
> 'etc')
>
> That doesn't make it any prettier.

Python 1.5.2 (#1, Sep 17 1999, 20:15:36) [GCC egcs-2.91.66 19990314/Linux
(egcs
- on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> print "foo%dbar" % 23
foo23bar
>>>


> As both forms seem equally capable to get the job done, and I can't see
> the one method being significantly more efficient that the other, in
> the end it would an argument over aesthetics.
>
> Aesthetic argument 1: I don't like data making an unneccesary roundtrip
> into a different domain. When using 's=n' numeric data (n) has to be
> expressed as a string.
>
> Aesthetic argument 2: The form 's=n' is in fact a tuple coded as a
> string, so why not get rid of the coding and make it explicit? After
> all, one of Pythons design paradigms would to be: "explicit is better
> than implicit".

Two good points (which really boil down to one). Counterpoints
would include "it's nicer to have all strings than some strings
and some tuples", and "other languages allowing such explicit
specs [C/C++ only, AFAIK] use the name=value syntax, so that will
be easier to read for people coming from those languages". But
I'm just arguing for argument's sake -- it's an obscure corner
of an obscure idiom, and both usages look pretty much OK to me
for something likely to see frequencies as low as this.


> Oddly enough, the thing which attracted me to Python was the C-meets-
> Lisp quality of its syntax. Both C and Lisp are - in my opnion - among
> the purest and most minimalist types of languages. Python seems to be
> able to combine the two extreme paradigms into an effective, yet clean
> language.

Nolo contendere -- and I'd add that C and Lisp seem to me to have
(opposite) serious syntax flaws, making their underlying semantic
simplicity harder to grasp/appreciate for some, while Python's
syntax is SO clean and neat.


Alex

Harald Hanche-Olsen

unread,
Jan 12, 2001, 6:49:56 PM1/12/01
to
+ rcameszR...@dds.removethistoo.nl (Robert Amesz):

| Alex Martelli wrote:
|
| Ah, Lisp, so beautiful, so pristine, so ... verbose. Well, it's great
| for lists, but anything numeric is a chore. Let's see:
| (EQ (PLUS 1 1) 2)
| t
| Phew, so least that's still true... ;-)

Which is an accident of the implementation, in the same sense that
the following is accidentally true in python:
>>> 1+1 is 2
1

I'm sure you meant to write (= (+ 1 1) 2) ?

One neat thing about Lisp though, is how you can rewrite the syntax,
for example to allow you to write infix arithmetic if you wish. This
takes much of the pain out of involved numerical work. I wrote some
scheme macros once (many years ago) to accomplish this - it was only a
moderate amount of work. In Common Lisp you can accomplish much the
same thing even more elegantly using reader macros.

but-now-I-have-a-feeling-we're-in-the-wrong-newsgroup-ly y'rs,
--
* Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/>
- Yes it works in practice - but does it work in theory?

Robert Amesz

unread,
Jan 12, 2001, 9:28:19 PM1/12/01
to
Alex Martelli wrote:

> [Does the % operator need a tuple?]


>
>>>> print "foo%dbar" % 23
>foo23bar
>>>>

Ah, I stand corrected. And I really should have checked this myself,
sorry about that. Especially as that would have taken less time than
typing that part of the reply. Silly me.


> [Arguing aesthetics]


>
>Counterpoints would include "it's nicer to have all strings than
>some strings and some tuples", and "other languages allowing such
>explicit specs [C/C++ only, AFAIK] use the name=value syntax, so
>that will be easier to read for people coming from those
>languages".

Those are good points as well. As was the case with mine, these too are
aesthetic arguments, and discussions about personal preferences isn't
going to settle the issue, which is a minor one anyway.


>But I'm just arguing for argument's sake -- it's an obscure corner
>of an obscure idiom, and both usages look pretty much OK to me for
>something likely to see frequencies as low as this.

True. Personally, I'd say this 'obscure corner' has gotten as much
attention as it warrants.


Robert Amesz

Patricia Hawkins

unread,
Jan 13, 2001, 1:23:12 PM1/13/01
to
>>>>> "JD" == Jan Dries <jan....@dcube-resource.be> writes:

JD> I once wrote a class to simulate enums. Using it, you can write:

JD> colors = enum("GREEN","BLUE","RED","YELLOW")

JD> and you can subsequently refer to the enum constants as:

JD> x = colors.GREEN

JD> If no value is supplied, the first name is assigned 0, the second 1 and
JD> so on. But you can supply values, as in:

JD> colors = enum("GREEN=1","BLUE=0x08","RED","YELLOW")

Well, that implements most of _this_ thread, right there.

I'd put off studying __setattr__ -- thanks, that's a nice example of
how to use it to prevent unwanted access.

Hope you don't mind, but in the course of playing with it, I added
type checking, improved the exception handling, and allowed split to
only split once, so that "PURPLE=3=4=5" will be a syntax error --
code, like cats, should barf easily, and without much distress.

Of course, you can still say:
colors = enum("1=64", "GREEN=1","BLUE=0x08","RED","YELLOW")
but if the language allows it, who am I to disallow it?

# Jan Dries <jan....@dcube-resource.be>
# To: pytho...@python.org
# Date: Sun, 07 Jan 2001 22:09:22 +0100
# Organization: D-Cube Resource
#
#
# PJH -- 1/13/2001
# *Added type check -> raise TypeError.
# *substitued AccessError for AttributeError,
# so as to report the exact crime.
# *Added maxsplit argument to split, now eval will check syntax
# for us.
#

import exceptions
from types import *
import re

class enum:
def __init__(self,*names):
self.__mnames = {}
value = 0;

for i in names:
pairs = re.split("\s*[=]\s*",i, 1)


if len(pairs) == 2:
value = eval(pairs[1])

if type(value) not in (IntType, LongType):
raise TypeError, "Non-integer type %s: %s"%(type(value),str(value))



self.__mnames[pairs[0]] = value
value += 1

def __getattr__(self,name):
try:
return self.__mnames[name]
except:
raise AttributeError

def __setattr__(self,name,value):
if name == "_enum__mnames":
self.__dict__[name] = value
else:

raise AccessError, "You may not change %s.%s "%(self, name)

class AccessError(exceptions.Exception):
pass

Robert Amesz

unread,
Jan 13, 2001, 5:40:53 PM1/13/01
to
Harald Hanche-Olsen wrote:

>+ rcameszR...@dds.removethistoo.nl (Robert Amesz):
>
>| Alex Martelli wrote:
>|
>| Ah, Lisp, so beautiful, so pristine, so ... verbose. Well, it's
>| great for lists, but anything numeric is a chore. Let's see:
>| (EQ (PLUS 1 1) 2)
>| t
>| Phew, so least that's still true... ;-)
>
>Which is an accident of the implementation, in the same sense that
>the following is accidentally true in python:
>>>> 1+1 is 2
>1

In python you're quite right, as this example shows:

>>> 101+101 is 202
0

>I'm sure you meant to write (= (+ 1 1) 2) ?

Oh, sure, if you insist on using *modern* Lisp idiom. <g>

Regarding the EQ, IIRC for numerical arguments it should do a
normal comparison, at least in the dialect I used some years ago.
(Remember XLisp?) For lists it only returns t if they're strictly
equal, not structurally equal, so in that respect it works the same
as 'is' in Python. I could (maybe should) have used EQUAL or = of
course, but, as you might have guessed, I felt like being
deliberately obtuse. ;-)


>One neat thing about Lisp though, is how you can rewrite the syntax,
>for example to allow you to write infix arithmetic if you wish.
>This takes much of the pain out of involved numerical work. I wrote
>some scheme macros once (many years ago) to accomplish this - it was
>only a moderate amount of work.

True, the paradigm of Lisp is that all data structures are lists - or
cons-cells, rather - including the language itself.


>In Common Lisp you can accomplish much the same thing even more
>elegantly using reader macros.

The problem with reader macros is, however, that there's (generally)
no way of translating back. You could be in for a shock if you need
to debug that code later on. Mind you, similar things can be said if
you're a C-programmer and put too much code into your #define macros.
And, though it isn't mentioned often, the same goes for C++'s
templates.


>but-now-I-have-a-feeling-we're-in-the-wrong-newsgroup-ly y'rs,

Right you are. Back to Python it is.


Robert Amesz

Steve Holden

unread,
Jan 15, 2001, 7:40:06 PM1/15/01
to
Alex Martelli wrote:
>
[ ... ]
> But I'm just arguing for argument's sake -- it's an obscure corner
> of an obscure idiom, and both usages look pretty much OK to me for
> something likely to see frequencies as low as this.
>
No, you aren't.


Gareth McCaughan

unread,
Jan 21, 2001, 5:03:21 PM1/21/01
to
Robert Amesz wrote:

> Ah, Lisp, so beautiful, so pristine, so ... verbose. Well, it's great
> for lists, but anything numeric is a chore. Let's see:
> (EQ (PLUS 1 1) 2)
> t
> Phew, so least that's still true... ;-)

Er, you mean


(= (+ 1 1) 2)

not
(eq (plus (1 1) 2))
actually.

If you do a lot of numeric stuff and find Lisp syntax
a chore, you can define some read-macros to let you say
something like

[1+1==2]

instead. There's something called "mathtran" that does
this sort of thing, though I think its syntax is a little
different.

Interested parties may wish to see
http://web.ukonline.co.uk/g.mccaughan/g/remarks/with-open-file.html
for some further remarks on the illusion of Lisp's verbosity. Or not,
as the case may be. :-)

--
Gareth McCaughan Gareth.M...@pobox.com
sig under construc

0 new messages