string formatting with mapping & '*'... is this a bug?

Alex Martelli aleaxit at yahoo.com
Fri Sep 10 17:39:26 EDT 2004


Pierre Fortin <pfortin at pfortin.com> wrote:
   ...
> > >          self.__dict__.update(locals())
> > 
> > Ah, sorry, my favourite idiom to avoid the boilerplate of
> > 
> >     def __init__(self, fee, fie, foo, fum):
> >         self.fee = fee
> >         self.fie = fie
> >         self.foo = foo
> >         self.fum = fum
> > 
> > Boilerplate is bad, and I'm keen on "Once, and ONLY once!" as a
> > programming practice, so having to mention each of those names three
> > times in totally repetitive ways makes me shiver.  My favourite idiom
> 
> I fully agree with "once and ONLY once"...  but you've pointed out that
> "not-at-all is better than once"... :^)

Well, there is 'once' here -- the names of the attributes are listed
once, in the argument list of 'def __init__'

> > does, per se, leave a silly self.self around (which means a silly
> > reference loop) so I should remember to 'del self.self' after it...
> 
> Since "self" is a convention (could use "this"), t'would be nice if Python
> could avoid foo.foo ref. loops in a future release...

I don't see how to do that cleanly, simply and within general rules.
"Special cases are not special enough to break the rules"...


> > >     map = dict(Date=Date,
> > >                Open=float(Open),
> > >                High=float(High),
> > >                Low=float(Low),
> > >                Close=float(Close),
> > >                Volume=int(Volume),
> > >                AdjClose=float(AdjClose),
> > >                Change=change[int(float(AdjClose) >= float(Open)) +
> > >                              int(float(AdjClose) == float(Open))]
> > >                )
> > 
> > I could suggest some deboilerplatization here, too, but maybe that would
> > be pushing things too far...
> 
> Hey! You're on a roll and I'm open for more abu... er education if this
> isn't an imposition... :^) 

Sure, see later.

> > it IS a Python oddity that True+False
> > equals 1;-).
> 
> That's OK with me...  it was True+True equals *1* (OR v. +) that forced me
> to wrap them with int()'s 

Nope:

>>> True+True
2

so I'm not sure what you mean...?

Anyway, back to map.  You have a bunch of local variables you want to
also set into a dict, except that most of them will need to be passed
through some one-argument function first. Change can fall into that
pattern if you just compute a local variable of that name just before
you compute map, so assume you've done that.

Now, once, outside of the loop, you prepare...:

howto_makemap = dict(Date=str, Volume=int,
                                      Change=change.__getitem__)

then, when it's time to prepare map,

map = {}
for k, v in locals():
    try: map[k] = howto_makemap.get(k, float)(v)
    except (ValueError, TypeError): pass

voila -- that's all you need.  You may end up with a few extraneous
entries in map (corresponding to local variables that just happen to be
castable into a float but have nothing to do with 'map') but that ain't
gonna break anything.


Whether I'd do this in production code -- I dunno.  It does reduce
boilerplate, but unfortunately it can only do this via a CLEVER trick.
Now CLEVER is a _criticism_, for code that's meant to be read and
maintained... something that happens over and over, like the
self.__dict__update idea, can be forgiven some cleverness, but a one-off
need is *probably* best met with simplicity even if that does mean some
boilerplate.  After all, if it's clever, 8 months from now when you need
to touch your code again you'll have a hard time figuring out what you
were doing here.  Good code is linear and obvious, either because of
intrinsic simplicity or familiarity through idiomatic (frequent) use...

So, in production code, I think I'd eschew this cleverness and rather
take the boilerplate hit.  Simplicity never comes easy to those of us
who are born with a tortuous mind, but it's still worth striving for!


Alex



More information about the Python-list mailing list