Python: Capitalize a word using string.format()
Asked Answered
S

4

26

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.

Sp answered 25/7, 2013 at 2:51 Comment(5)
Why are you arbitrarily recasing usernames?Sanitation
@IgnacioVazquez-Abrams Username is actually equal to either a username or a pronoun -- for pronouns, one wouldn't always capitalize them.Sp
Wait... you'd change the display case of a username, but not a pronoun? Why would you do that? People expect their usernames to be exactly the case they typed into the box.Penelope
@Penelope Not in this project :p No, seriously, I'm working with a dataset where I know for a fact that each username is capitalized already (it's required by the software, which I didn't write).Sp
I have a use case where it would be extremely convenient to be able to upper case using a format specifier as I use format_map and supply a dict-like object that does a lot of various kinds of work to bring back a value with many other values in the format template. I can't write to the dict-like not simply like that. I don't want to have to parse the string template just to find out what values I need to pull out of the dict-like in advance just so I can case one of the values first - that would defeat the whole point.Tien
S
11

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)
Sanitation answered 25/7, 2013 at 3:2 Comment(2)
And just for reference in case anyone else has this question, here is the function that needs to be changed.Sp
s/changed/overridden/Sanitation
A
19

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)
Animadvert answered 11/9, 2017 at 16:41 Comment(2)
I believe your example has an error in it. Instead of having return value as the last line of convert_field, you should have returned the output of calling super, i.e.: return super(ExtendedFormatter, self)...Rhett
@Rhett indeedSchnitzler
I
13

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.

Inky answered 25/7, 2013 at 3:20 Comment(1)
I like this approach! Yeah, I'm doing it on a larger scale (600+ messages, and various different functions -- not just capitalization), so it's not really applicable to my situation per say, but still something to keep in mind.Sp
S
11

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)
Sanitation answered 25/7, 2013 at 3:2 Comment(2)
And just for reference in case anyone else has this question, here is the function that needs to be changed.Sp
s/changed/overridden/Sanitation
R
6

In python 3.6+ you can use fstrings now. https://realpython.com/python-f-strings/

>>> txt = 'aBcD'
>>> f'{txt.upper()}'
'ABCD'
Rosebay answered 18/7, 2020 at 21:10 Comment(1)
This would only solve the problem if OP inlined the f-string into random.choice, which he may or may not be able to do.Viceroy

© 2022 - 2024 — McMap. All rights reserved.