string formatting with mapping & '*'... is this a bug?
Bengt Richter
bokr at oz.net
Fri Sep 10 01:46:23 EDT 2004
On Fri, 10 Sep 2004 00:06:43 -0400, Pierre Fortin <pfortin at pfortin.com> wrote:
>On 10 Sep 2004 00:42:19 GMT Bengt 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...
>> >
>> Well, you could subclass str and override its __mod__ method ;-)
>> If you had class format(str): ..., you could then write
>> format('%(key)*.*f") % map
>> and have it do whatever you like.
>
>This must be one of those days when my density goes up.... :^/
>
>Can you fill in the blanks on this...? Mismatched quotes aside, I just
Oops on the quotes ;-/
>don't seem to grok how map's key gets in there since there is a ")"
>between the format string and "%" -- then, I still don't see how "*.*"
>gets set as it would in: "%*.*f" % (width,max,float)
>
>I'll try to finally get a handle on subclassing/overriding with this....
>:>
I was making a quick comment that if you really wanted to you could
intercept all the arguments and interpret them as you wish. The line I wrote
would demand some conventions about what was in the map, or indeed it could
be a custom mapping object. To see the arguments, we can do (starting with
your original post example mapping):
>>> mapping = {'one':1,'two':2}
>>> "%(two)d" % mapping
'2'
>>> "%(two)6d" % mapping
' 2'
And you showed that:
>>> "%(two)*d" % (mapping,6)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: format requires a mapping
Let's see:
>>> class format(str):
... def __mod__(self, *a, **kw): print repr(self), a, kw
...
>>> format("%(two)*d") % mapping
'%(two)*d' ({'two': 2, 'one': 1},) {}
But we need an argument for the '*':
>>> format("%(two)*d") % (mapping,6)
'%(two)*d' (({'two': 2, 'one': 1}, 6),) {}
Ok, we see that we have the custom string back as self, and a is the args tuple
for the rhs of the % operator, and there are no keyword args.
But we need a way to pick up the '*' values. I think the most straightforward way
would be to have a mapping argument be the first, for %(name)...x lookups, and
optional additional arguments like an ordinary tuple, like
>>> format('%(key)*.*f') % ({'key':2.3}, 7, 3)
'%(key)*.*f' (({'key': 2.2999999999999998}, 7, 3),) {}
The intent being like
>>> '%7.3f'%2.3
' 2.300'
You could go hog wild and allow multiple mapped values in a single
format, as in this contrived combination:
>>> format('%(key)*.("three")f') % ({'key':2.3, 'three':3}, 7)
'%(key)*.("three")f' (({'three': 3, 'key': 2.2999999999999998}, 7),) {}
BTW, note that a mapping only has to support __getitem__, so you can make a
custom mapping to synthesize interesting named things or find them in interesting
places, and even have special methods that could be called from format.__mod__ for
whatever you think up. Just so your end product is somewhat explainable.
This contrivance illustrates custom mapping as a one-liner class instance
returning the map key uppercased instead of looking something up.
>>> '%(hi)s %(ho)8s' % type('',(),{'__getitem__':lambda s,x: x.upper()})()
'HI HO'
It's more readable if you define a class as usual ;-)
>>> class U(object):
... def __getitem__(self, key): return key.upper()
...
>>> umap = U()
>>> '%(hi)s %(ho)8s' % umap
'HI HO'
Custom stuff tends to be slower than builtins written in C, of course.
I'll leave the rest as an exercise from here ;-)
Regards,
Bengt Richter
More information about the Python-list
mailing list