In last week's Python snippet we took a look at some formatting options for numbers using Pythons Format Specification Mini Language. This week we're going to continue with formatting, but this time we're going to look at nested string interpolation.

String interpolation basics

Just so we're all on the same page, string interpolation is where we put a string inside another string. For example, like this:

name = "Jose"
print(f"Hello, {name}")  # Hello, Jose

We can also use the format method:

name = "Jose"
print("Hello, {}".format(name))  # Hello, Jose

The result is exactly the same, f-strings are just a newer syntax available from Python 3.6 onward.

In both versions we take the string "Hello, {}", where the curly braces are a placeholder. Then we interpolate (insert) the string assigned to the variable name, replacing the placeholder curly braces.

Nested string interpolation

An interesting thing is that we can actually perform string interpolation inside one of these placeholder curly braces. Now, why on Earth would you ever want to do that?

Well, for one, it allows us to dynamically set formatting options when using the Format Specification Mini Language. For example, perhaps we are dynamically creating file names with includes a numbering scheme, and we want our program to be as generic as possible. We therefore perhaps don't want to hardcode the numbering scheme.

We're going to use a simplified example here, where the other aspects of the filename never change, but you could easily replace those aspects as well.

First, the hardcoded version, so we know what to compare it to:

number_of_files = 3

for file_number in range(1, number_of_files + 1):
	print(f"image{file_number:03}.png")

# image001.png
# image002.png
# image003.png

Now the nested version:

number_of_files = 3
number_digits = int(input("How many digits are used in the numbering scheme? "))

for file_number in range(1, number_of_files + 1):
	print(f"image{file_number:0{number_digits}}.png")

Now if the user enters 5, we'll get a naming scheme like this: image00001.png.

We could make various improvements to this, such as adding a try / except block for the user input to catch invalid values, but this is enough to demonstrate the concept.

Nested string interpolation with format

We can also perform the same kind of nested string interpolation with the format method.

If we use positional arguments, the nested placeholders are considered to come directly after the placeholder they inhabit. So the example from above would look like this using positional arguments:

number_of_files = 3
number_digits = int(input("How many digits are used in the numbering scheme? "))

for file_number in range(1, number_of_files + 1):
	print("image{:0{}}.png".format(file_number, number_digits))

I think this is a little confusing, so if you have to use format for some reason, I'd opt for keyword arguments like this:

number_of_files = 3
number_digits = int(input("How many digits are used in the numbering scheme? "))

for file_number in range(1, number_of_files + 1):
	print("image{number :0{padding_amount}}.png".format(
		number=file_number,
		padding_amount=number_digits
	))

This way there can be no confusion about what belongs where. That being said, f-strings are clearly the much cleaner option in this case if you can use them.

Wrapping Up

I hope you enjoyed this Python snippet post and learnt something new. It's a bit of a niche topic this week, but it's something that a lot of people don't know about, and it's certainly very useful in some cases.

As always, we're offering readers of our blog our Complete Python Course for just $9.99. If you follow the link in this post the coupon code BLOGGER will be applied for you automatically. You get lifetime access to the course, including any future updates, so if you're looking to upgrade your Python skills, it's definitely a great place to start. Hopefully we'll see you there!