Python - convert to string with formatter as parameter - python

I am trying to write a function which takes two arguments:
an object
a formatter string (as specified in docs)
which then returns a formatted string:
What I tried is sort of:
def my_formatter(x, form):
return str(x).format(form)
What I am expecting is:
s = my_formatter(5, "%2f")
# s = 05
t = my_formatter(5, "%.2")
# t = 5.00
etc...
The format function unfortunately does not work like that. Any ideas?

For that style of formatting you'd have to use the string % values string formatting operation:
def my_formatter(x, form):
return form % x
You'd also have to alter your format; to get 05 you'd have to use "%02d", not "%2f".
You were getting confused by the str.format() method, which uses a different formatting syntax, and you got the arguments swapped; you'd use form.format(x) instead.
You probably want to look into the built-in format() function here; the syntax is slightly different, but offers more features:
>>> format(5, '02d')
'05'
>>> format(5, '.2f')
'5.00'
That's pretty close to what you were already using, minus the %.

Related

Trying to convert a string into a list in Pandas

I am trying to convert a column, which looks something like this
cleaned
['11-111']
['12-345', '67-789']
['00-000', '01-234']
into a list, since I read that Pandas initially interprets list as strings from this article:
https://towardsdatascience.com/dealing-with-list-values-in-pandas-dataframes-a177e534f173
I using the function mentioned in the article
master["cleaned"] = master["cleaned"].apply(eval)
but I am getting this error
eval() arg 1 must be a string, bytes or code object
I tried looking them up, but I can't figure it out
df.cleaned = pd.eval(df.cleaned)
There doesn't appear to be a built it to deal with failures, so you can make it yourself:
def try_eval(x):
try:
return eval(x)
except:
return x
df.cleaned = df.cleaned.apply(try_eval)
Then you can look for the ones that didn't convert by doing:
df.cleaned[df.cleaned.apply(lambda x: isinstance(x, str))]

Format **kwargs to f-string

I am trying to format keyword arguments passed to a function, in such a way that the string output (has to be f-string in this case) would look like arg1=arg1_value, arg2=arg2_value,...
Apparently it is not possible to just do:
out = f'{**kwargs}'
so I have managed to do it in this way:
def to_string(**kwargs):
return ', '.join([f'{k}={v}' for k,v in kwargs.items()])
> to_string(a=1, b=2)
'a=1, b=2'
Is there a way to do this more simply?
You can unpack an iterable (cf [another question]) but not a dictionnary.
Because you want the exact same output as f"{a=}, {b=}, {c=}" you will not find a solution much shorter than what you already have.

Creating new conversion specifier in Python

In python we have conversion specifier like
'{0!s}'.format(10)
which prints
'10'
How can I make my own conversion specifiers like
'{0!d}'.format(4561321)
which print integers in following format
4,561,321
Or converts it into binary like
'{0!b}'.format(2)
which prints
10
What are the classes I need to inherit and which functions I need to modify? If possible please provide a small example.
Thanks!!
What you want to do is impossible, because built-in types cannot be modified and literals always refer to built-in types.
There is a special method to handle the formatting of values, that is __format__, however it only handles the format string, not the conversion specifier, i.e. you can customize how {0:d} is handled but not how {0!d} is. The only things that work with ! are s and r.
Note that d and b already exist as format specifiers:
>>> '{0:b}'.format(2)
'10'
In any case you could implement your own class that handles formatting:
class MyInt:
def __init__(self, value):
self.value = value
def __format__(self, fmt):
if fmt == 'd':
text = list(str(self.value))
elif fmt == 'b':
text = list(bin(self.value)[2:])
for i in range(len(text)-3, 0, -3):
text.insert(i, ',')
return ''.join(text)
Used as:
>>> '{0:d}'.format(MyInt(5000000))
5,000,000
>>> '{0:b}'.format(MyInt(8))
1,000
Try not to make your own and try to use default functions already present in python. You can use,
'{0:b}'.format(2) # for binary
'{0:d}'.format(2) # for integer
'{0:x}'.format(2) # for hexadecimal
'{0:f}'.format(2) # for float
'{0:e}'.format(2) # for exponential
Please refer https://docs.python.org/2/library/string.html#formatspec for more.

Use parameter in format string

This:
print '{:x<4d}'.format(34)
prints this:
34xx
How can I do this:
width = 13
print '{:x<|width|d}'.format(34)
to get this:
34xxxxxxxxxxx
You can put one format field inside of the other:
>>> width = 13
>>> print '{:x<{}d}'.format(34, width)
34xxxxxxxxxxx
>>>
From the docs:
A format_spec field can also include nested replacement fields within
it. These nested replacement fields can contain only a field name;
conversion flags and format specifications are not allowed. The
replacement fields within the format_spec are substituted before the
format_spec string is interpreted. This allows the formatting of a
value to be dynamically specified.
Note however that the nesting can only go one level deep.
This works
('{:x<%dd}' % width).format(34)
this will work:
>>> width = 13
>>> print '{:x<{}d}'.format(34,width)
34xxxxxxxxxxx
You can nest arguments in format, using kwargs allows you to be more explicit and less susceptible to confusing results:
fillchar = 'x'
width = 13
print "{:{f}<{w}d}".format(34, w=width, f=fillchar)

Python: Capitalize a word using string.format()

Is it possible to capitalize a word using string formatting? For example,
"{user} did such and such.".format(user="foobar")
should return "Foobar did such and such."
Note that I'm well aware of .capitalize(); however, here's a (very simplified version of) code I'm using:
printme = random.choice(["On {date}, {user} did la-dee-dah. ",
"{user} did la-dee-dah on {date}. "
])
output = printme.format(user=x,date=y)
As you can see, just defining user as x.capitalize() in the .format() doesn't work, since then it would also be applied (incorrectly) to the first scenario. And since I can't predict fate, there's no way of knowing which random.choice would be selected in advance. What can I do?
Addt'l note: Just doing output = random.choice(['xyz'.format(),'lmn'.format()]) (in other words, formatting each string individually, and then using .capitalize() for the ones that need it) isn't a viable option, since printme is actually choosing from ~40+ strings.
As said #IgnacioVazquez-Abrams, create a subclass of string.Formatter allow you to extend/change the format string processing.
In your case, you have to overload the method convert_field
from string import Formatter
class ExtendedFormatter(Formatter):
"""An extended format string formatter
Formatter with extended conversion symbol
"""
def convert_field(self, value, conversion):
""" Extend conversion symbol
Following additional symbol has been added
* l: convert to string and low case
* u: convert to string and up case
default are:
* s: convert with str()
* r: convert with repr()
* a: convert with ascii()
"""
if conversion == "u":
return str(value).upper()
elif conversion == "l":
return str(value).lower()
# Do the default conversion or raise error if no matching conversion found
return super(ExtendedFormatter, self).convert_field(value, conversion)
# Test this code
myformatter = ExtendedFormatter()
template_str = "normal:{test}, upcase:{test!u}, lowcase:{test!l}"
output = myformatter.format(template_str, test="DiDaDoDu")
print(output)
You can pass extra values and just not use them, like this lightweight option
printme = random.choice(["On {date}, {user} did la-dee-dah. ",
"{User} did la-dee-dah on {date}. "
])
output = printme.format(user=x, date=y, User=x.capitalize())
The best choice probably depends whether you are doing this enough to need your own fullblown Formatter.
You can create your own subclass of string.Formatter which will allow you to recognize a custom conversion that you can use to recase your strings.
myformatter.format('{user!u} did la-dee-dah on {date}, and {pronoun!l} liked it. ',
user=x, date=y, pronoun=z)
In python 3.6+ you can use fstrings now. https://realpython.com/python-f-strings/
>>> txt = 'aBcD'
>>> f'{txt.upper()}'
'ABCD'

Categories

Resources