Reputation: 60044
I would like to define my own str.format() specification, e.g.
def earmuffs(x):
return "*"+str(x)+"*"
to be used, e.g., like this:
def triple2str(triple, fmt="g"):
return "[{first:{fmt}} & {second:+{fmt}} | {third}]".format(
first=triple[0], second=triple[1], third=triple[2], fmt=fmt)
so that:
## this works:
>>> triple2str((1,-2,3))
'[1 & -2 | 3]'
>>> triple2str((10000,200000,"z"),fmt=",d")
'[10,000 & +200,000 | z]'
## this does NOT work (I get `ValueError: Invalid conversion specification`)
>>> triple2str(("a","b","z"),fmt=earmuffs)
'[*a* & *b* | z]'
The best I could come up with so far is
def triple2str(triple, fmt=str):
return "[{first} & {second} | {third}]".format(
first=fmt(triple[0]), second=fmt(triple[1]), third=triple[2])
which works like this:
>>> triple2str((1,-2,3))
'[1 & -2 | 3]'
>>> triple2str((10000,200000,"z"),fmt="{:,d}".format)
'[10,000 & 200,000 | z]' # no `+` before `2`!
>>> triple2str((10000,200000,"z"),fmt=earmuffs)
'[*10000* & *200000* | z]'
Is this really the best I can do?
What I am unhappy about is that that it is unclear how to incorporate the modifiers (e.g., +
above).
Is str.format
extensible?
Upvotes: 0
Views: 362
Reputation: 3049
In python 3 you can call functions in f-strings
, perhaps this can help.
def earmuffs(val):
return "*{}*".format(val)
form = lambda a: f"method {earmuffs(a[0])} and method {earmuffs(a[1])}"
b = ('one', 'two')
form(b)
>>>'method *one* and method *two*'
Upvotes: 1
Reputation: 4189
str.format
itself is not extensible. However, there are two ways:
1.
Use your custom string formatter: https://docs.python.org/2/library/string.html#custom-string-formatting
Override the format_field(obj, format_spec)
method to catch the callable format_spec. Then call your formatter directly.
This code snippet can help you (it works with py 3.5 & 2.7 at least):
import string
class MyFormatter(string.Formatter):
def __init__(self, *args, **kwargs):
super(MyFormatter, self).__init__(*args, **kwargs)
self.fns = {}
def format_field(self, value, format_spec):
# print(repr(value), repr(format_spec))
# intercept {fmt} part with functions:
if callable(value):
result = super(MyFormatter, self).format_field(value, format_spec)
self.fns[result] = value
return result
# intercept {var:{fmt}} with remembered {fmt}:
if format_spec in self.fns:
return self.fns[format_spec](value)
else:
return super(MyFormatter, self).format_field(value, format_spec)
def fn(val):
return '*{}*'.format(val)
f = MyFormatter()
print(f.format("{var:{fmt}}", var=1000, fmt='g'))
print(f.format("{var:{fmt}}", var=1000, fmt=fn))
2.
Define per-object format method __format__(self, format_spec)
, where format_spec
is whatever goes after :
in e.g. {var:g}
. You can format self-presentation of the object as you wish.
However, in your case, the objects are ints/strs, not the custom objects, so this method will also not help much.
As the conclusion:
Yes, your solution in the question is sufficient and probably the simplest one.
Upvotes: 2