In this tutorial we will learn about different string formatting technique available in Python
- old method i.e.
percent(%) formatting
(supported in Python 2 and 3) - new method i.e.
string.format()
(Python 2.6 and up) - latest method i.e.
f-strings
- formatted string literal (Python 3.6 and up)
Percent % formatting
An old, deprecated way of formatting strings, which you might end up seeing in old code, is the C language style %
formatting. In this method, you use the %
operator to pass in values. Inside the string, you use the %
character, followed by a format specifier, to declare how the value should be inserted;
Example-1: Substitute values using % formatting
In this example we will perform basic substitution of strings and integer.
#!/usr/bin/env python3 name = 'deepak' drink = 'coffee' price = 3 message = 'Hey %s, this %s costs $%d.' % (name, drink, price) print(message)
Output from this script:
~]# python3 percent-formatting.py Hey deepak, This coffee costs $3.
Here,
- That
%s
inside the string means to interpolate a string%d
means replace it with an integer - The number of
%
appearances in the string needs to match the number of data items after the%
that follows the string. - A single data item such as
name
goes right after that final%
. - Multiple data must be grouped into a tuple as we have added name, drink, price inside parenthesis separated by commas
The following table shows the possible formatting of substitution keys.
Conversion | Meaning |
---|---|
%d | Signed integer decimal |
%i | Signed integer decimal |
%u | Obsolete — identical to 'd' |
%o | Signed octal value |
%x | Signed hexadecimal — lowercase |
%X | Signed hexadecimal — uppercase |
%f | Floating point decimal |
%e | Floting point exponential—lowercase |
%E | Floating point exponential—uppercase |
%g | Floating point format—uses lowercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise |
%G | Floating point format—uses uppercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise |
%c | Single character |
%r | String (converts valid Python object using repr()) |
%s | String (converts valid Python object using str()) |
%% | No argument is converted, results in a '%' character |
Example-2: Add padding and align the content for strings
You can add other values in the format string between the % and the type specifier to designate minimum and maximum widths, alignment, and character filling.
- An initial '
%
' character. - An optional alignment character: nothing or '
+
' means right-align, and '-
' means left-align. - An optional minwidth field width to use.
- An optional '
.
' character to separate minwidth and maxchars. - An optional maxchars (if conversion type is s) saying how many characters to print from the data value. If the conversion type is
f
i.e. floating integer, this specifies precision (how many digits to print after the decimal point).
This may sound confusing and all theoretical, so let's use this in our next example:
#!/usr/bin/env python3 name = 'root' message1 = 'How' message2 = 'are you?' ## no extra padding log = "Hey %s, %s %s " % (name, message1, message2) print(log) ## added extra padding before message1 log = "Hey %s, %10s %s " % (name, message1, message2) print(log) ## added extra padding before message1 and right aligned log = "Hey %s, %+10s %s " % (name, message1, message2) print(log) ## added extra padding before message1 and left aligned log = "Hey %s, %-10s %s " % (name, message1, message2) print(log) ## Max 3 characters allowed in message2 log = "Hey %s, %s %.3s " % (name, message1, message2) print(log) ## min width is 10 and max character allowed is 3 in message2 log = "Hey %s, %s %10.3s " % (name, message1, message2) print(log)
Output from this script:
~]# python3 string-formatting.py
Hey root, How are you?
Hey root, How are you?
Hey root, How are you?
Hey root, How are you?
Hey root, How are
Hey root, How are
Here,
- First print statement is the default output with no additional formatting
- Second print statement - we have added additional padding whitespace. Since we have not specified + or -, the default padding is in the left side
- Third print statement - We are using + sign to align the text in the right side with a defined padding width to provide respective whitespace so the output is similar to second print statement
- Fourth print statement - We are using - sign to align the text in the left side with a defined padding width to provide respective whitespace so this time
message1
is aligned in the left with additional space in the right side. - Fifth print statement - We have defined a character limit in message2 where . (dot) acts as a separator. So only first 3 letters of message 2 is printed. Since I have not defined any padding so the texts are printed with default padding
- Sixth print statement - I have combined the character limit with minimum width (width.char limit) so now we see that
message2
contains additional whitespace padding in the left
Example-3: Add extra whitespace and alignment changes using float numbers
Now we will repeat the same exercise we did with strings in the previous example, the only difference is here we take floating number and different message to log.
#!/usr/bin/env python3 name = 'root' float_num = 95.2 ## no extra padding log = "Hey %s, the number is: %f. Cheers!" % (name, float_num) print(log) ## added extra padding before float_num log = "Hey %s, the number is: %10f. Cheers!" % (name, float_num) print(log) ## ads a + sign to float_num. No whitespace added log = "Hey %s, the number is: %+10f. Cheers!" % (name, float_num) print(log) ## added extra padding after float_num in the right side as the text is left aligned log = "Hey %s, the number is: %-10f. Cheers!" % (name, float_num) print(log) ## Max 3 numbers allowed in float_num log = "Hey %s, the number is: %.3f. Cheers!" % (name, float_num) print(log) ## min width is 10 before float_num and max character allowed is 3 in float_num log = "Hey %s, the number is: %10.3f. Cheers!" % (name, float_num) print(log)
Output from this script:
~]# python3 string-formatting.py
Hey root, the number is: 95.200000. Cheers!
Hey root, the number is: 95.200000. Cheers!
Hey root, the number is: +95.200000. Cheers!
Hey root, the number is: 95.200000 . Cheers!
Hey root, the number is: 95.200. Cheers!
Hey root, the number is: 95.200. Cheers!
I have added comments with each print statement that should be self explanatory. The output seems different compared to what we had with strings.
String formatting using string.format()
- Python 3.x uses a different formatting system, which is more powerful than the system that Python 2.x uses.
- The print statement is now a function.
- Format strings use the curly brackets "
{}
" to create the replacement fields. - Anything that is not contained within the brackets will be considered a literal and no conversation will be done on it.
- If you have the need to include curly brackets as a literal, you can escape it by using '{{' and '}}.'
- This formatting system has been backported to Python 2.6 and Python 2.7.
The basic default format string is like this:
#!/usr/bin/env python3 name = 'deepak' num = 4359 print('Hello {}, your roll number is {}'.format(name, num))
Output from this script:
~]# python3 positional-arg.py
Hello deepak, your roll number is 4359
Here {}
considers the order in which .format()
function provides the placeholders. If we change the order of .format()
values:
#!/usr/bin/env python3 name = 'deepak' num = 4359 print('Hello {}, your roll number is {}'.format(num, name))
Output from this script:
~]# python3 positional-arg.py
Hello 4359, your roll number is deepak
So the order of the output also has changed along with the .format()
placeholders.
To have more control on how string.format()
works, the string formatting supports two different types of placements"
- Positional arguments: We must specify the index position
{index}
to perform the format - Keyword arguments: Here we will replace key with the value where the value can be accessed with key of parameter inside curly braces
{key}
Positional Arguments
With positional argument we provide the index position inside the curly brackets {}
. These placeholders are replaced by respective values from .format()
function based on the index position. Just like Python list, the index value here starts with 0 for the first position.
We will take the existing example where we had changed the order of values inside .format()
but this time we will provide the positional argument inside {}
so that proper values are placed in the template.
#!/usr/bin/env python3 name = 'deepak' num = 4359 print('Hello {1}, your roll number is {0}'.format(num, name))
Output from this script:
~]# python3 positional-arg.py
Hello deepak, your roll number is 4359
But if you provide the index position which doesn't exist in the .format()
function tuple then you will get IndexError
Traceback (most recent call last):
File "positional-arg.py", line 6, in
print('Hello {2}, your roll number is {0}'.format(num, name))
IndexError: tuple index out of range
Keyword Arguments
A keyword argument is a name-value pair that you pass to a template. You directly associate the name and the value within the argument, so when you pass the argument to the template, there’s no confusion. Keyword arguments free you from having to worry about correctly ordering your arguments in the message template, and they clarify the role of each value in the print statement or template.
In this example I have used {name}
and {num}
as the keyword placement and these arguments are defined in .format()
function for the replacement
#!/usr/bin/env python3 print('Hello {name}, your roll number is {num}'.format(name='deepak', num=4395))
Output from this script:
~]# python3 keyword-arg.py
Hello deepak, your roll number is 4395
But if you refer to a key which is not defined then you will get KeyError
. In this example I have replaced num
with test
which is not defined and hence we get KeyError
.
Traceback (most recent call last):
File "keyword-arg.py", line 3, in
print('Hello {name}, your roll number is {test}'.format(name='deepak', num=4395))
KeyError: 'test'
Advanced formatting with string.format()
Similar to %
formatting we can achieve more specific layouts by adding extra syntax in the format string. For the formatting method, we use a colon after the possibly empty substitution target’s identification, followed by a format specifier that can name the field size, justification, and a specific type code.
Here’s the formal structure of what can appear as a substitution target in a format string—its four parts are all optional, and must appear without intervening spaces:
{fieldname component !conversionflag :formatspec}
In this substitution target syntax:
- fieldname is an optional number or keyword identifying an argument
- component is a string of zero or more “.name” or “[index]” references used to fetch attributes and indexed values of the argument, which may be omitted to use the whole argument value.
- conversionflag starts with a
!
if present, which is followed byr
,s
, ora
to callrepr
,str
, orascii
built-in functions on the value, respectively. - formatspec starts with a
:
if present, which is followed by text that specifies how the value should be presented, including details such as field width, alignment, padding, decimal precision, and so on, and ends with an optional data type code.
Following are some of the format Specifiers using examples
Specifier | Description |
---|---|
:<20 |
Left Align to a width of 20 |
:>20 |
Right Align to a width of 20 |
:^20 |
Center Align to a width of 20 |
:06.2f |
Zero pad with precision for floating point number |
:*>10 |
Asterisk pad right align to a width of 10 |
:=10 |
Padding is placed after the sign, if any but before digits. ONLY works for numeric types |
:+20 |
Force a sign before the number left padded to a width of 20 |
:−20 |
Force a sign before negative numbers only, left padded to a width of 20 |
: 20 |
Force a leading space on positive numbers or “-“ on negative numbers, left padded to a width of 20 |
:, |
Force thousands comma for numeric |
:.2% |
Expresses percentage (.975 results in 97.50%) |
:%M/%d/%Y |
Type specific usage. In this case datetime |
0:#x |
Formats an integer as a hex value 0xhh |
0:#o |
Formats an integer as an octal value 0oxx |
0:#b |
Formats an integer as a binary value 0bxxxxxx |
Example-1: Add extra whitespace padding to strings and integers using string.format()
We will use the advanced formatting syntax to add additional whitespace and padding to strings and integers in this python script:
Output from this script:
~]# python3 string-format-1.py
Hello deepak, your roll number is 43578. Best of Luck
Hello deepak, your roll number is 43578. Best of Luck
Hello deepak , your roll number is 43578 . Best of Luck
Hello deepak , your roll number is 43578 . Best of Luck
Hello deepak, your roll number is +43578. Best of Luck
Hello deepak, your roll number is 43578. Best of Luck
You can check the comments for individual print statement which explains the output.
Example-2: Advanced formatting with floating numbers
In this example I have used string and floating point number. We are adding different types of alignment and formatting in each print statement.
#!/usr/bin/env python3 name = 'deepak' num = 97.65 ## no additional formatting print('Hello {0}, you got {1} marks'.format(name, num)) ## add width of 10 characters for both placeholders print('Hello {0:10}, you got {1:10} marks'.format(name, num)) ## Right align to the width of 10 print('Hello {0:>10}, you got {1:>10} marks'.format(name, num)) ## Center align to the width of 10 print('Hello {0:^10}, you got {1:^10} marks'.format(name, num)) ## Left align to the width of 10 print('Hello {0:<10}, you got {1:<10} marks'.format(name, num)) ## Assigned a floating point format to num print('Hello {0}, you got {1:f} marks'.format(name, num)) ## truncating the floating point number to two characters after decimal for num print('Hello {0:s}, you got {1:.2f} marks'.format(name, num)) ## adds a field with a width of six characters and zero padding on the left for num print('Hello {0:s}, you got {1:06.2f} marks'.format(name, num))
Output from this script:
~]# python3 string-format-2.py
Hello deepak, you got 97.65 marks
Hello deepak , you got 97.65 marks
Hello deepak, you got 97.65 marks
Hello deepak , you got 97.65 marks
Hello deepak , you got 97.65 marks
Hello deepak, you got 97.650000 marks
Hello deepak, you got 97.65 marks
Hello deepak, you got 097.65 marks
Using f-strings for string formatting
f-strings
appeared in Python 3.6, and are now the recommended way of formatting strings.
To make an f-string:
- Type the letter f or F directly before the initial quote.
- Include variable names or expressions within curly brackets
({})
to get their values into the string.
It’s like the previous section’s “new-style” formatting, but without the format()
function, and without empty brackets ({}
) or positional ones ({1}
) in the format string.
This is a simple example where we are using keyword arguments instead the keywords are defined as variable:
#!/usr/bin/env python3 name = 'deepak' num = 12345 print(f'Hello {name}, Your roll number is {num}')
Output from this script:
~]# python3 f-string-ex.py
Hello deepak, Your roll number is 12345
Formatting with f-strings
is shorter than using C-style format strings with the % operator
and the str.format
method in all cases
Example-1: Add additional padding and alignment using f-strings
We can use similar syntax as we used with string.format()
to add additional alignment and padding using f-strings
:
#!/usr/bin/env python3 name = 'deepak' num = 12345 ## default output without any additional formatting print(f'Hello {name}, Your roll number is {num}') ## add width of 10 characters for both placeholders print(f'Hello {name:10}, Your roll number is {num:10}') ## Right align to the width of 10 print(f'Hello {name:>10}, Your roll number is {num:>10}') ## Center align to the width of 10 print(f'Hello {name:^10}, Your roll number is {num:^10}') ## Left align to the width of 10 print(f'Hello {name:<10}, Your roll number is {num:<10}') ## Add asterisk to the integer and right align the content to total 10 char print(f'Hello {name}, Your roll number is {num:*>10}') ## Add zeroes to the integer and right align the content to total 10 char print(f'Hello {name}, Your roll number is {num:0>10}')
Output from this script:
~]# python3 f-string-ex.py
Hello deepak, Your roll number is 12345
Hello deepak , Your roll number is 12345
Hello deepak, Your roll number is 12345
Hello deepak , Your roll number is 12345
Hello deepak , Your roll number is 12345
Hello deepak, Your roll number is *****12345
Hello deepak , Your roll number is 0000012345
Conclusion
In this tutorial, we took an in-depth tour of the string object type. We learned about coding string literals, and we explored string operations, including sequence expressions, string method calls, and string formatting with both expressions and method calls. We also now know that f-strings are succinct yet powerful because they allow for arbitrary Python expressions to be directly embedded within format specifiers.