Today we’ll be talking about the string format operator.
The string format operator is not the preferable way of formatting strings, but you can still find it in older code, so it’s useful to know something about it. That said, keep using the format method and f-strings in your work.
If you haven’t read my previous articles on formatting strings, feel free to read them. Here’s the article on the basics of formatting with the format method and f-strings, here’s the article on advanced string formatting with the format method and here’s the article on using dictionaries to format strings.
Let’s start with the symbol of the string format operator. It’s a % character. We also call this method of formatting string interpolation because the operator interpolates other data types like integers, floating point numbers and others into the formatted string.
Table of Contents
The Syntax
We use a format string (e.g. %s) inside the string as a placeholder and the % operator after the string to separate it from the values that are to be inserted in place of the placeholders. The format string is also referred to as the format symbol or format placeholder.
The syntax of the string format is:
%[flags][width][.precision]type
All these elements are optional except type. In the simplest case we thus have the following syntax:
%type
And let’s have a look at the simplest form first. We use the format string %s for strings. Lowercase ‘s’ is used for the string type. The values may be literals or variables. Here’s an example with a literal. In the output the literal string “Amanda” will replace the placeholder:
>>> print("Her name was %s, I think." % "Amanda") # literal string
Her name was Amanda, I think.
Here’s an example with a variable:
>>> name = "Chris"
>>> print("And her husband's name was %s." % name) # variable
And her husband's name was Chris.
It also works with echoing:
>>> country = "Austria"
>>> "She's from %s or Nigeria, I'm not sure." % country
"She's from Austria or Nigeria, I'm not sure."
Multiple Values to Insert
If there are more values to insert, we use tuples. We use the format string %d for integers:
>>> print("I'm %s and I'm %d years old." % ("Jack", 64))
I'm Jack and I'm 64 years old.
Here is the same example with matching colors so that it’s easier to see which value matches which format string:
Another example:
>>> personal_data = ("Mike", 28, "Amsterdam", 3)
>>> print("My name is %s, I'm %d, I live in %s and I have %d kids." % personal_data)
My name is Mike, I'm 28, I live in Amsterdam and I have 3 kids.
Let’s add some colors to this example as well:
Types of Format Strings
Depending on the data type we need, we can use several format strings:
%s is used for strings like above.
%d or %i is used for integers.
%c is used for single characters.
The characters may be given as one-letter strings:
>>> print("This starts with the letter %c." % "A")
This starts with the letter A.
or integers corresponding to the values of the letters:
>>> print("This starts with the letter %c." % 65)
This starts with the letter A.
%o is used for octal integers.
%x is used for hexadecimal numbers (with lowercase letters).
%X is used for hexadecimal numbers (with uppercase letters).
>>> number = 124
>>> print("decimal: %d || octal: %o || hexadecimal: %x or %X" % (number, number, number, number))
decimal: 124 || octal: 174 || hexadecimal: 7c or 7C
%f is used for floats:
>>> weight = 2
>>> print("It weighs exactly %f ounces." % weight)
It weighs exactly 2.000000 ounces.
%e and %E are used for exponential notation with lowercase and uppercase E respectively:
>>> weight = 0.0000412
>>> print("The weight of the objects is %e grams." % weight)
The weight of the objects is 4.120000e-05 grams.
>>> print("The weight of the objects is %E grams." % weight)
The weight of the objects is 4.120000E-05 grams.
%g and %G are used if we want the shorter of %f and %e / %E respectively.
So, if we used %e with the number 60, we would get 6.000000e+01, which is longer than 60. %g uses the shortest possible notation.
As to the other number, 1000000000 (a billion), it’s shorter in exponential notation. That’s why it’s chosen:
>>> numbers = (60, 1000000000)
>>> print("number 1: %g || number 2: %g" % numbers)
number 1: 60 || number 2: 1e+09
>>> print("number 1: %G || number 2: %G" % numbers)
number 1: 60 || number 2: 1E+09
Strings in Triple Quotes
We can also format strings in triple quotes:
>>> brothers = ("Mike", "Luke", "Steven")
>>> text = """
... The three brothers,
... %s, %s and %s,
... are all well-known doctors
... and top specialists.
... """
>>> print(text % brothers)
The three brothers,
Mike, Luke and Steven,
are all well-known doctors
and top specialists.
More Advanced Formatting
In all the examples above we were using just the basic format string syntax. But, as we remember, we can put much more information about the string format into the placeholder.
Let’s have a look at an example with an integer number. Here’s the basic form again:
>>> print("They have %d cars." % 3)
They have 3 cars.
Now we can add the width to the format string. We want the output number to be 4 characters wide. What we get is the number 3 preceded by 3 blanks, so 4 characters altogether:
>>> print("They have %4d cars." % 3)
They have 3 cars.
Let’s now have a look at an example with a float number. Here we have a program saved to a file. By the way, the string to be printed may consist of just the format string like below:
a = 196.56477
b = 0.012
c = 14.1
d = 1952
print("%8.3f" % a)
print("%8.3f" % b)
print("%8.3f" % c)
print("%8.3f" % d)
And here’s the output:
196.565
0.012
14.100
1952.000
So, what happened? The format string for all the numbers is “%8.3f”, which means the output should be 8 characters wide, out of which the last 3 characters are decimal places. If the total width is less than 8, blanks are added on the left. If due to precision some of the decimal places must be cut off, the number is rounded, like in the first example. The final width includes all the digits, the decimal point and the blanks.
So, the string means: a float number, 8 characters wide, with precision 3. Here’s how it can be visualized for our four numbers in the output:
There’s, however, one thing to note: although the decimal places are rounded within the precision set in the format string and the superfluous ones are trimmed, the integer part of the number (the one before the decimal point) is left intact even if the width should exceed the width set in the placeholder.
Here we have two numbers. The first one is an integer, which will be formatted as a float with width 4 and precision 1, thus one blank will be added at the beginning:
>>> print("%4.1f" % 2)
2.0
Now, if the integer part takes up more than width characters, it’ll be maintained. The precision will be taken to cinsideration:
>>> print("%4.1f" % 114525774.4457)
114525774.4
Let’s have a look at octal and hexadecimal numbers now. Here’s an example we were talking about a while ago:
>>> print("octal: %o || hexadecimal: %x or %X" % (124, 124, 124))
octal: 174 || hexadecimal: 7c or 7C
Here we have just the basic format strings %o, %x and %X used for octal and hexadecimal numbers. Now let’s add width and precision. Here width determines how many characters there should be altogether. If necessary, blanks will be added on the left. Precision determines how many characters should be used to output the number and if necessary, zeros will be added:
>>> print("%10.6o" % (124)) # octal
000174
>>> print("%10.6x" % (124)) # hexadecimal with lowercase letters
00007c
>>> print("%10.6X" % (124)) # hexadecimal with uppercase letters
00007C
Let’s visualize this too:
Flags
Now that we’ve seen examples with format strings including width and precision, there’s still one thing we can add to the placeholder. It’s the flag. Flags are used immediately after the % sign.
Here are the flags:
#
if used with octal and hexadecimal numbers, the value is preceded by 0o, 0x or 0X:
>>> print("%#5o" % 50) # oct 50 preceded by 0o
0o62
>>> print("%#5x" % 50) # hex 50 preceded by 0x
0x32
>>> print("%#5X" % 50) # hex 50 preceded by 0X
0X32
Now let’s set the width to 5 characters and precision to 3. That’s why we get 3 characters (032) after the 0X prefix:
>>> print("%#5.3X" % 50) # hex 50 preceded by 0X
0X032
And now the width is 10 (hence the blanks on the left) and the precision is 6 (hence 6 characters after 0x):
>>> print("%#10.6x" % 50) # hex 50 preceded by 0x
0x000032
0
if used with numbers, the output will be padded with zeros on the left: Here’s an integer number with width 6. Without the flag, the output is padded with blanks:
>>> print("%6d" % 8)
8
With the flag the output is padded with zeros:
>>> print("%06d" % 8)
000008
Here’s an example with a float without the flag:
>>> print("%6.2f" % 8.56214)
8.56
and with the flag:
>>> print("%06.2f" % 8.56214)
008.56
-
this flag’s task is to left justify the string:
Here the number is 6 characters wide, with precision 2, left justified:
>>> print("We bought %-6.2f grams of the substance." % 8.56214)
We bought 8.56 grams of the substance.
+
if used with a number, a sign character will precede the string:
>>> print("%+4.2f" % 3.151147)
+3.15
(space)
if used with a number, a blank will precede the string:
Here’s what it looks like without the space flag:
>>> print("%4.2f" % 3.151147)
3.15
And here’s the same with a space flag:
>>> print("% 4.2f" % 3.151147)
3.15
Formatting Outside the print Function
String formatting doesn’t have to be inside the print function. We can assign a formatted text to a variable and then pass the variable to the print function:
>>> text = "They need %4.2f or so kilograms of %s." % (6.478854, "oil")
>>> print(text)
They need 6.48 or so kilograms of oil.
or even like so:
>>> print('He said: "%s"' % text)
He said: "They need 6.48 or so kilograms of oil."
where the already formatted text is inserted into a placeholder.
Here’s the video version of this article: