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