4 Ways To Perform Python String Formatting

Photo by Artturi Jalli on Unsplash

String formatting can be pretty fundamental and simple, but what really triggered me was what was the best way to do the formatting while writing the code for specific situations. And that curiosity kinda let me down the rabbit hole.

On a side note, I actually wrote other Python related articles before like Python Number Formatting Binary Type which probably needs me to write another one more on just Python 3. If you are also keen, I wrote this piece on writing messages in binary just for fun. Other than that, I did write mostly on Java and a few on JavaScript, feel free to check out!


String formatting in general is a process that evaluates your string literal which contains placeholders that will eventually be replaced with your corresponding values. What this means is that you might have something like:

“I am [your_name] and I am [age] years old.”

So, in the string example above, you can see that your_name and age are the placeholders in which can be substituted by whatever inputs or values you want them to be. For the above case, you can replace the name with your name and age with your age number.

Python basically allows you to write a string like that and also replace the values when you run it. This can really help you to dynamically update the values depending on whoever the person is, instead of having to fix the name and age in place.

I had written String formatting on Java before, so let’s see how Python does it. I will also try to focus more on Python 3 now instead of Python 2.

With that said, there are about 4 ways to do string formatting in Python. Here are the list:

  1. %-formatting
  2. str.format()
  3. f-string
  4. string.Template

If you are wondering how to say those out loud, here are how I call them:

  1. Percentage formatting
  2. Dot format
  3. F string
  4. String Template

Yeah, it works for me so far, but if you have better suggestions, please leave your comments down below!


1. %-Formatting

I remember I learned this when Python 2 was still around and people mostly still use Python 2. So, how this % operator works is that it makes use of positional formatting. Positional formatting is a type of formatting in which it replaces your placeholders based on the order or position of your values, kind of.

Let me show you an example:

name = ‘John’
friend = ‘Sarah’
“I am %s and I am with %s.” % (name, friend)

# Output
I am John and I am with Sarah.

Let’s take a look at the example above again but I swap the name and friend around:

“I am %s and I am with %s.” % (friend, name)

# Output
I am Sarah and I am with John.

Notice how the two values turn out to be swapped when I swap them? Yes, this is because of positional formatting. It takes in the first value passed to it and replaces the first placeholder respectively, and so on and so forth. First to first, second to second, etc.

Now, the above example only deals with one type, the String value type or denominated by %s under % operator. What about other types? Well, % operator provides different placeholders for different types too like:

  1. %s — String
  2. %d — Integers
  3. %f — floating points numbers
  4. %x / %X — integers in hexadecimal representation (both lowercase and uppercase)
  5. %.<no_of_digits>f — floating points numbers with specified number of digits. I think by default there are 6 decimal digits.

Let’s look at another example:

name = ‘John’
age = 23
"I am %s and I am %d years old." % (name, age)

# Output
I am John and I am 23 years old.

Notice how the values are replaced successfully based on the placeholder type. So, if you swap the name and age while leaving the placeholders’ positions as the above, it will throw an error because %s is for string type but you supply it with integer instead. Remember how the thing is positional formatting, so first to first matching, second to second matching, etc.

If you swap the %s and %d while leaving the name and age the same, it will still throw an error due to type mismatch.

More examples for you to understand:

name = ‘John’
age = 23
friend = ‘Sarah’
money = 68.88
print(‘I am %s and I am %d years old.’ % (name, age))
print(‘I am %s and I am with %s.’ % (name, friend))
print(‘I am %s and I am with %s.’ % (friend, name))
print(‘You are %s and your current balance is %f.’ % (name, money))
print(‘You are %s and your current balance is %.3f.’ % (name, money))

# Output
I am John and I am 23 years old.
I am John and I am with Sarah.
I am Sarah and I am with John.
You are John and your current balance is 68.880000.
You are John and your current balance is 68.880.

This other example is also interesting because it can have a named placeholder but yet it still utilises positional formatting. Let’s take a look:

"I am %(my_name)s and my friend is %(friend_name)s." % {'my_name':name, 'friend_name':friend}

It will still produce the same results as the one without the naming. However, I rarely use this nor have I seen this used often as you still can’t swap it around. So there is that inflexibility to it in my opinion. That is the main thing as to why I pretty much don’t use it often, or at all.


2. Str.format()

This one though, I do use it relatively often when things are simple and not complex. Also, people can understand faster when they read the code. I mean, it says “format” there.

The basic syntax for this one is like this:

[your_string].format()

There are 2 ways of formatting here. First one is positional formatting, whereby the placeholders are replaced accordingly. The second one is what I would say formatting based on the name of the placeholders. This latter one simply means that even though the position of your values are not in order, you still can replace them correctly if the values are referred to the naming of the placeholders accordingly.

Let’s see how the positional formatting works:

name = ‘John’
age = 24
print('I am {} and {}.'.format(name, age))
print('I am {} and {}.'.format(age, name))

# Output
I am John and 24.
I am 24 and John.

Now let’s take a look at formatting by referencing the names instead:

print('I am {your_name} and {your_age}.'.format(your_name=name, your_age=age))
print('I am {your_name} and {your_age}.'.format(your_age=age, your_name=name))

# Output
I am John and 24.
I am John and 24.

Pretty simple and straightforward right? Moreover, the second way really gives you that flexibility that you don’t really need to care about the order and I personally like this one more.

Other examples with a bit of description:

# positional arguments
print("Hello {}, your current balance is {}.".format("John", 288.1234))
# index arguments
print("Hello {0}, your current balance is {1}.".format("John", 288.1234))
print("Hello {1}, your current balance is {0}.".format("John", 288.1234))
# keyword arguments
print("Hello {name}, your current balance is {blc}.".format(name="John", blc=288.1234))
# mixed arguments
print("Hello {0}, your current balance is {blc}.".format("John", blc=288.1234))

# Output
Hello John, your current balance is 288.1234.
Hello John, your current balance is 288.1234.
Hello 288.1234, your current balance is John.
Hello John, your current balance is 288.1234.
Hello John, your current balance is 288.1234.

Despite all the pros, people might still not want to use str.format(). One of the things that people might not like is its verbosity. For example, the text value is repeated here:

value = 5 * 10
'The value is {value}.'.format(value=value)

# Output
'The value is 50.'

Even in its simplest form there is a bit of boilerplate, and the value that’s inserted into the placeholder is sometimes far removed from where the placeholder is situated:

'The value is {}.'.format(value)
'The value is 80.'

Honestly, I don’t find the issues above bothering me, but who knows, it is up to you.


3. F-String

This type is called so, literally because you write a string with f in the front which is then followed by either a single quotation mark or triple quotation mark with the string value sandwiched inside. It is an expression evaluated at run time.

Here is the syntax:

f’I am {name} and {age} years old.’

So, this type of formatting lets you embed your Python expressions inside the string itself. Using the syntax/example above, we can see that I embed the variables straight into the string. Run it and you will get this without error:

# Output
I am John and 24 years old.

This way of formatting is really amazing in the sense that it can be very flexible. Not only you can format a whole template with just that, you can do arithmetics directly inside too. Just like:

a = 5
b = 10
print(f‘Basic arithmetic gets you {a * b} and {2 * b + a}.’)

# Output
Basic arithmetic gets you 50 and 25.

So, what about formatting a whole template then? This one has something to do with the f-string with triple quotation mark. By using triple instead of single quotation mark, it can actually display the string that you write exactly. Let’s see what I mean below:

name = 'Mark'
friend = 'John'
a = 5
b = 10
output = f'''
My friend is {friend}
and I am {name}
and we are doing simple math
which yields {a * b}.
'''
print(output)

# Output
My friend is John
and I am Mark
and we are doing simple math
which yields 50.

As you can see, it follows even the white spaces without having to use \n, etc. This one is really useful when you need to do complex formatting. For the simple ones though, you can use the one with single quotation mark should be sufficient.

Backslashes may not appear inside the expression portions of f-strings, as it may produce errors. That means you cannot use them, for example, to escape quotes:

$ f'{\'quoted string\'}'
File "<stdin>", line 1
SyntaxError: f-string expression part cannot include a backslash

I actually used to use this a lot until recently. I think str.format() is also fine and I just use it interchangeably.

In addition, it is said that f-strings are faster than the other two most commonly used string formatting mechanisms: %-formatting and str.format().


4. String Template

Another option to format a string in Python. It is supposedly a somewhat simpler syntax and functionality, but that’s for you to judge.

Personally, I don’t really work with this often after knowing the other 3 string formatting ways, but I can see why it can be simpler to use because of the syntax and circumstance. Let’s take a look.

In order to use this, you need to import the Template in. Let’s see the syntax:

from string import Template
Template(‘Your string here: $ans’).substitute(ans=’yes’)

So, after looking at the syntax, it can probably be likened to str.format() due to it having a bit of similarity to it.

The second thing that is obvious besides the use of Template and the structure, is the placeholder. Instead of using curly brackets, {}, it is using the dollar sign, $. So if we look at the simplicity of the placeholder, it is probably similarly to the % used in %-formatting.

However, if you want to use curly brackets, that’s also possible. It’s just that it is no different as compared to without curly brackets if you use it like this:

‘You are ${adj}’

That is the same as:

‘You are $adj’

So when do we use the curly brackets? When there are letters following afterwards.

‘You are ${adj}.’
‘When you want to do ${noun}fication.’

Pardon the random sentences but, that’s when you include the brackets. It is to identify that that’s the word or keyword that will be replaced. Otherwise, it might be interpreted wrongly that instead of nounfication instead of noun that is the placeholder. For adj., it might still work as only valid characters are considered. However, for safety, it is better to just use the curly brackets. Without curly brackets, you may risk causing errors.

Here is to examples:

Template('$obj is $colour').substitute(obj='Car',colour='black')
Template('$obj. is $colour').substitute(obj=’It’,colour='blue')
Template('${noun}ification').substitute(noun='Ident')

# Output
Car is black
It. is blue
Identification

The cool thing about this is that you can actually use dictionary as the substitute instead of defining the key-value one by one like above. Let’s take a look at an example:

dict_var = dict(name=’Mark’, friend=’John’)
Template(‘The $friend has $name as a friend.’).substitute(dict_var)

# Output
That John has Mark as a friend.

So, in order to use dictionary instead, make sure the keys are the same as the placeholders. Python will take care of the rest.

Another thing that you might want to take note of is the use of escaping. What happens if you would like to use the dollar sign? It’s basically just like any other formatting way, just escape the character by doubling it up. Here is what I mean:

Template(‘$name owes you $$$amount.’).substitute(name=’John’, amount=100)

# Output
John owes you $100.

Notice how I use two $ to print out one $ while the third $ simply indicates that it is a placeholder keyword that is going to be replaced with my value.

For more reference on this, you can access the Template String documentation.


Wrap-Up

In this article, we saw the different formatting ways available in Python. We also saw various syntaxes that can be used to format Strings and the conversion types that can be used for different data types. Hopefully you got to know them better.

That’s all for today’s learning. If you spotted something that I am missing in this article, feel free to share it in the comment section below. We would all love to learn together.

Thanks for reading.