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

Alex Martelli aleaxit at yahoo.com
Fri Sep 10 10:34:31 EDT 2004


Pierre Fortin <pfortin at pfortin.com> wrote:
   ...
> > [[you can actually simplify your fmt dict a lot thanks to the
> > defaults]], and then code the printing like:
> 
> AH-HA!!!!  anything _not_ specified is defaulted...  took me a minute to
> realize that I needed to remove the EXplicits in favor of the IMplicits...
> not just remove their values...  DUH!!   Nice!!
> 
> MANY THANKS!!

You're welcome!

> 
> Pierre
> 
> PS:  Here's the latest incarnation of the test script...  for me, the
> "voodoo" part is
>          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
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...
       

>    Not sure I could have found that, since without it, the error:
>      Traceback (most recent call last):
>        File "./foo", line 40, in ?
>          print ( "%(Date)ss %(Open)sf %(High)sf %(Low)sf "
>        File "./foo", line 7, in __getitem__
>          return "%%(%s)%d.%d" % (varname,
>      AttributeError: formatter instance has no attribute 'format_map'
>    seems cryptic until one realizes it's an initilization problem... 
>    Right?  

Right, you have to self.format_map=format_map and so on, or shortcut
that through the above-mentioned idiom.

>     Date, Open, High, Low, Close, Volume, AdjClose = D.split(',')
> 
>     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...

>     
>     print ( "%(Date)ss %(Open)sf %(High)sf %(Low)sf "
>             "%(Close)sf %(Volume)sd %(AdjClose)sf %(Change)ss"
>             % fmt % map )
> ----------------------
> 
> MUCH simpler/cleaner!! Though I gotta understand how the 2 mappings got
> used. Is it as simple as print() iterating over N mappings, taking the
> next character(s) for the format..?  Elegant if so... though more than 2
> mappings could be mind-bending... :^)

No, print itself has nothing to do with it; % is left associative, so

    print something % fmt % map

is equivalent to, say:

    temp1 = something % fmt
    temp2 = temp1 % map
    print temp2

Come to think of that, you SHOULD hoist the "real format" out of the
loop, what I'm calling temp1 (silly name) in this snippet -- the fmt
does not change between legs of the loop, so you don't really want to
recompute the identical temp1 thousands of times (premature optimization
is the root of all evil in programming, sure, but here, this simple
'hoist a constant out of the loop' is just avoiding waste... waste not,
want not... -- and in some sense splitting the format computation out of
the formatting makes things _simpler_!).

 
> Note that I changed "%%(Change)s" to likewise get rid of that oddity...

Yep, makes things more regular.  Though the computation of Change itself
is strange, but, whatever... it IS a Python oddity that True+False
equals 1;-).

 
> This has turned into quite a "less is more" education for me...  :^)
> 
> Thanks again!

And again, you're welcome!


Alex



More information about the Python-list mailing list