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

Pierre Fortin pfortin at pfortin.com
Fri Sep 10 06:46:59 EDT 2004


On Fri, 10 Sep 2004 08:27:44 +0200 Alex wrote:

> Pierre Fortin <pfortin at pfortin.com> wrote:
>    ...
> > I was hoping to use the likes of:  "%(key)*.*f" % map
> > however, unlike with the non-(key) formats, there appears to be no way
> > to specify a "*.*" size when a map is used...
> 
> It is not clear to me where you would expect to get the values for those
> stars from -- 'map' being a dictionary it has no concept of "ordering",
> so there is no concept of "next value" after map['key'].  If I had this
> kind of problem I would doubtlessly tack it with _two_ formattings,
> first one to get the values for the stars (with explicit names), giving
> a clean format string, then another to get the value for the data.  Say:
> 
> '%%(key)%(width)d.%(prec)df' % map % map
> 
> Not all that readable, alas.  Alternatively, you could use a non-keyed
> format string and extract things from the map on the RHS...:
> 
> '%*.*f' % [map[x] for x in ('key', 'width', 'prec')]
> 
> I personally consider this a bit more readable, but that's quite
> debatable, of course...
> 
> 
> Alex

Thanks to you and Bengt for your patience with this problem...

Since I'd already dipped my toe in the dual-map water in an earlier post,
a "Duh!" moment occured to me as I read your responses...  Realizing that
examples rarely show the true complexity of a problem, the answer (for me)
lay in keeping the formatting and data maps separate...  I find mapped
strings immensely more readable/maintainable than the simple same-quantity
and gotta-be-in-the-right-order ones:

   change=["down","up","unchanged"]
   """On %10s, the closing price of $%6.2f was %s
   from the day's open of $%6.2f 
   ....
   ....
   """ % (Date, Close, change[int(float(Open>= ...], Open, ... )

especially when the string needs to be reworked by re-ordering, adding
and/or deleting items.

Here's my reasonably (to me :) readable/maintainable solution to this core
problem...  Only the "fmt" statement needs tweaking to generate the
desired appearance.

#!/usr/bin/env python

def mkdict(**kwargs):
    return kwargs

fmt = { 'wDate':10, 'wOpen':6, 'wHigh':6, 'wLow':6,  # width
        'wClose':6, 'wVolume':10, 'wAdjClose':6,
        'pDate':10, 'pOpen':2, 'pHigh':2, 'pLow':2,  # precision
        'pClose':2, 'pVolume':0, 'pAdjClose':2 }

# data will be read from several thousand files
sampledata = [
    "9-Sep-04,19.49,20.03,19.35,19.93,60077400,19.93",
    "8-Sep-04,18.96,19.53,18.92,18.97,52020600,18.96",
    "7-Sep-04,18.98,19.18,18.84,18.85,45498100,18.84",
    ]

change=["down","up","n/c"]

for D in sampledata:
    Date, Open, High, Low, Close, Volume, AdjClose = D.split(',')

    map = mkdict(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))]
                 )
    
    # line continuations must be left-justified to avoid extra spaces
    print "%%(Date)%(wDate)d.%(pDate)ds \
%%(Open)%(wOpen)d.%(pOpen)df \
%%(High)%(wHigh)d.%(pHigh)df \
%%(Low)%(wLow)d.%(pLow)df \
%%(Close)%(wClose)d.%(pClose)df \
%%(Volume)%(wVolume)d.%(pVolume)dd \
%%(AdjClose)%(wAdjClose)d.%(pAdjClose)df \
%%(Change)s \
" % fmt % map


Generates:
  9-Sep-04  19.49  20.03  19.35  19.93   60077400  19.93 up
  8-Sep-04  18.96  19.53  18.92  18.97   52020600  18.96 n/c
  7-Sep-04  18.98  19.18  18.84  18.85   45498100  18.84 down

At least, now I understand why mappings are the exception to the "*[.*]"
rule of formatting... :^)

Thanks!
Pierre



More information about the Python-list mailing list