Topic 2: Printing and Print Formatting using .format()

The Basic Python3 print() function

For most programs you will want some way to display results. In the last topic discussion we used the built-in print() function, and passed it an object without any formatting. This is a nice benefit of Python's adaptive interpretation of objects. You can pass it an object, without telling it what kind of object it is, and Python will figure out a way to display it. You can also pass it several objects, separated by commas, and it will display each one separated by a space. When printing this way, there are limited ways to affect the formatting that Python will decide to use. For example, changing the spacing in the print function argument list has no effect.

x = 9
y = 4.4
z = 'peanut butter'
print(x, y, z)
print(x,      y,z)

All of these produce the same result. When passing a list of objects, Python controls the formatting and output.

9 4.4 peanut butter
9 4.4 peanut butter
9 4.4 peanut butter

NOTE: There is a way to pass additional arguments to the print() function to change the separator between objects and the end terminator. This is typically not done however since the .format() function, which will be discussed throughout this topic, provides better formatting options. There is one specific example where passing arguments to the print() function is useful, and that is to inhibit the new line terminator. This will be discussed later in this topic.

For simple outputs, there is still a lot that can be done without formatting controls. For example, we saw above that a string object can be printed alongside a number object. This can be used to provide useful string descriptions for a numerical value. The code below provides a nice output for an account balance. The only real complaint might be the space that is inserted between the dollar sign and the number, which is the separator inserted between the two objects.

balance = 234.98
print("Your account balance is: $", balance)

Your account balance is: $ 234.98

An example where this space might be a bigger problem is shown below. Suppose someone used a list of berry prefixes and wanted to create berry names on-the-fly, they would be unsatisfied by the results of just passing the two string objects to the print() function.

berry_types = ["blue", "straw", "cran"]
for type in berry_types:
    print(type, "berry")

blue berry
straw berry
cran berry

Something useful to know that can help solve this problem, is that the argument list of the print() function (or any function) doesn't have to be just a comma separated list of objects. You can put calculations, even other function calls in the argument list, and Python will send the result to the print() function. To fix the berry problem we can use the fact that the plus (+) operator can be used on strings to concatenate them together without any inserted separator, like a space. If the berry program is re-written as shown below, the calculation type + "berry" concatenates the two strings together, and passes the result, which is a single string object to the print() function. Viola! Problem solved.

berry_types = ["blue", "straw", "cran"]
for type in berry_types:
    print(type + "berry")


We can also use a similar technique to remove the space after the dollar sign when printing the account balance. If you just replace the comma with a plus (+) operator, you'll get an error that you can only concatenate str to str, not float. To resolve this, let's add another built-in Python function to our toolbox called str(). This function returns a string version of the object passed to it. This means that when we pass it a floating-point number, for example str(234.98) it returns an actual string of "234.98". Now we have two strings, and we can use the plus (+) operator to concatenate them. Here is the code and output for the two different versions.

balance = 234.98
print("Your account balance is: $", balance)
print("Your account balance is: $" + str(balance))

Your account balance is: $ 234.98
Your account balance is: $234.98

That pretty much covers the basics. But, before we declare victory... Let's try using what we know to print a sequence of numbers divided by 3. The output is shown below, and... oh, yuk! That looks terrible.

for x in range(10):
    print(x, "divided by 3 is:", (x/3))

0 divided by 3 is: 0.0
1 divided by 3 is: 0.3333333333333333
2 divided by 3 is: 0.6666666666666666
3 divided by 3 is: 1.0
4 divided by 3 is: 1.3333333333333333
5 divided by 3 is: 1.6666666666666667
6 divided by 3 is: 2.0
7 divided by 3 is: 2.3333333333333335
8 divided by 3 is: 2.6666666666666665
9 divided by 3 is: 3.0

Introduction to Formatting using the .format() function

There are a lot of ways to format text. We are going to talk about a string function called format(). Just before getting to that, notice that in the section title I used a dot (.) notation in front of the function name. That's how people typically refer to this function. This odd notation attributed specifically to this function is primarily because of the way that it is typically written. It is actually no different than any other member functions that can be applied to a string. To make this clear, let's talk about a different string function called replace(), and a Python function called dir() before moving on.

Let's start with dir(). This function attempts to return a list of valid attributes for the object provided as an argument. You can read this as, it provides a list of all of the things that you can do to the object. If I create a string variable, and then print the output of dir(), I get this.

str = "03-25-2018"

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', 
'__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', 
'__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', 
'__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
'__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 
'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 
'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 
'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 
'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 
'swapcase', 'title', 'translate', 'upper', 'zfill']

Wow! That's a lot of stuff. Most of which will have to wait for another topic discussion. You'll notice that format is in that list, but for this example let's pick replace. This should make it a little more obvious how the strange usage of these functions with strings work. Let's say that you encounter a set of dates in the format MM-DD-YYYY, but that you must convert them all to MM/DD/YYYY or it will break your database, or some other hypothetical badness. Not a problem, we'll use replace. The way this function works is that you provide the string to replace, and the replacement string, and Python will provide a new string with the requested replacements. But... Unlike a typical function where you would also pass the original string as an argument to the function, these functions are declared as a member function of the original string. The format is like this: Object.Function(Args). For example, converting the date would work like this.

str = "03-25-2018"
new_str = str.replace("-", "/")
print("Old format:", str)
print("New format:", new_str)

Old format: 03-25-2018
New format: 03/25/2018

Let's point out a couple things that may not have been obvious. First, the replace function returns a new string. It does not modify the original string. This is actually true of all string functions. Second, we used the dot (.) notation to call the replace() function as a member function of the string variable. We could actually call it directly using the string as well. This is part of Python's adaptive interpretation of what you want. Let's try that again a slightly different way. We will call the replace() function as a member function of the string itself, print the result, and we still get the right value. This is how the format() function is used as well. Most people put a space between the string and the .format() when it is written (because that's how they see it on the internet), and they think that .format() is somehow a self-contained separate function or keyword. It's not. It's just a string member function, and Python is ignoring the spaces. Go ahead and add several spaces between the string and the .replace() in the example below and see what happens. Yep, still works.

print("03-25-2018".replace("-", "/"))


Now let's try an example using format(). This function is applied as a member function of a string. It searches the string for special replacement and formatting characters inside of brackets {}, and returns a new string with the requested replacements. In the following example, I create a variable with a number that repeats infinitely, print the number using no formatting, and then print the number again using format(). In the example, the format string "{:0.3f}" tells the format() function to replace the bracketed expression with the floating point value, with only 3 decimal places. We'll talk the available options a little later. There are a lot of them.

num = (1/3)


We'll get into the details of what all of the formatting options are in a second. First, let's try printing the sequence of numbers divided by 3 again, this time using format(). The output is shown below, and... TaDa! Much better. I threw in a few extra pieces. This example contains regular text, and multiple brackets. That's OK. The first bracket has no formatting, which means that the resulting replacement would be the same as if you called str(x). Since the brackets are unnumbered (we'll talk about this soon), then format() will assume they increment by 1 starting at 0, and as long as the number of arguments provided equal the number of brackets, that's OK.

for x in range(10):
    print("{} divided by 3 is: {:0.3f}".format(x, (x/3)))

0 divided by 3 is: 0.000
1 divided by 3 is: 0.333
2 divided by 3 is: 0.667
3 divided by 3 is: 1.000
4 divided by 3 is: 1.333
5 divided by 3 is: 1.667
6 divided by 3 is: 2.000
7 divided by 3 is: 2.333
8 divided by 3 is: 2.667
9 divided by 3 is: 3.000

String Formatting Options

The grammar for the replacement field inside the brackets is shown below. The official Python documentation for Format String Syntax can be found here. This link points to the Python 3.7 doc link. At some point this link will become outdated. You can select a different version at the top left of the Python Docs web page.

replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
arg_name          ::=  [identifier | digit+]
attribute_name    ::=  identifier
element_index     ::=  digit+ | index_string
index_string      ::=  <any source character except "]"> +
conversion        ::=  "r" | "s" | "a"
format_spec       ::=  [[fill]align][sign][#][0][width][grouping_option][.precision][type]
fill              ::=  <any character>
align             ::=  "<" | ">" | "=" | "^"
sign              ::=  "+" | "-" | " "
width             ::=  digit+
grouping_option   ::=  "_" | ","
precision         ::=  digit+
type              ::=  "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | 
                       "o" | "s" | "x" | "X" | "%"

All of the options above probably look pretty daunting. Don't Panic! You'll probably never use half of them. In the rest of this section, we'll explore some of the more common options, and work through examples.

The [align], [sign], and [type] identifiers of the [format_spec]

The meaning of the various alignment options is as follows:

Option Meaning
'<' Forces the field to be left-aligned within the available space (this is the default for most objects).
'>' Forces the field to be right-aligned within the available space (this is the default for numbers).
'=' Forces the padding to be placed after the sign (if any) but before the digits. This is used for printing fields in the form ‘+000000120’. This alignment option is only valid for numeric types. It becomes the default when ‘0’ immediately precedes the field width.
'^' Forces the field to be centered within the available space.

Note that unless a minimum field width is defined, the field width will always be the same size as the data to fill it, so that the alignment option has no meaning in this case.

The sign option is only valid for number types, and can be one of the following:

Option Meaning
'+' indicates that a sign should be used for both positive as well as negative numbers.
'-' indicates that a sign should be used only for negative numbers (this is the default behavior).
space indicates that a leading space should be used on positive numbers, and a minus sign on negative numbers.

The '#' option causes the “alternate form” to be used for the conversion. The alternate form is defined differently for different types. This option is only valid for integer, float, complex and Decimal types. For integers, when binary, octal, or hexadecimal output is used, this option adds the prefix respective '0b', '0o', or '0x' to the output value. For floats, complex and Decimal the alternate form causes the result of the conversion to always contain a decimal-point character, even if no digits follow it. Normally, a decimal-point character appears in the result of these conversions only if a digit follows it. In addition, for 'g' and 'G' conversions, trailing zeros are not removed from the result.

The [type] identifier of the [format_spec] tells Python how you would like the object to be displayed. The options available are based on the type of object (string, integer or floating-point) that is being provided. If the identifier is not valid for the object being passed, Python will throw an exception, or it will try to implicitly modify the object to make it work. It is always better to be explicit. If you intend to use an identifier that is not compatible with the object, first modify the object using str(), or int(), or float() to convert it to the correct type.

The available string presentation types are:

Type Meaning
's' String format. This is the default type for strings and may be omitted.
None The same as 's'.

The available integer presentation types are:

Type Meaning
'b' Binary format. Outputs the number in base 2.
'c' Character. Converts the integer to the corresponding unicode character before printing.
'd' Decimal Integer. Outputs the number in base 10.
'o' Octal format. Outputs the number in base 8.
'x' Hex format. Outputs the number in base 16, using lower-case letters for the digits above 9.
'X' Hex format. Outputs the number in base 16, using upper-case letters for the digits above 9.
'n' Number. This is the same as 'd', except that it uses the current locale setting to insert the appropriate number separator characters.
None The same as 'd'.

In addition to the above presentation types, integers can be formatted with the floating point presentation types listed below (except 'n' and None). When doing so, float() is used to convert the integer to a floating point number before formatting.

The available presentation types for floating point and decimal values are:

Type Meaning
'e' Exponent notation. Prints the number in scientific notation using the letter 'e' to indicate the exponent. The default precision is 6.
'E' Exponent notation. Same as 'e' except it uses an upper case ‘E’ as the separator character.
'f' Fixed-point notation. Displays the number as a fixed-point number. The default precision is 6.
'F' Fixed-point notation. Same as 'f', but converts nan to NAN and inf to INF.
'g' General format. For a given precision p >= 1, this rounds the number to p significant digits and then formats the result in either fixed-point format or in scientific notation, depending on its magnitude.

The precise rules are as follows: suppose that the result formatted with presentation type 'e' and precision p-1 would have exponent exp. Then if -4 <= exp < p, the number is formatted with presentation type 'f' and precision p-1-exp. Otherwise, the number is formatted with presentation type 'e' and precision p-1. In both cases insignificant trailing zeros are removed from the significand, and the decimal point is also removed if there are no remaining digits following it.

Positive and negative infinity, positive and negative zero, and nans, are formatted as inf, -inf, 0, -0 and nan respectively, regardless of the precision.

A precision of 0 is treated as equivalent to a precision of 1. The default precision is 6.
'G' General format. Same as 'g' except switches to 'E' if the number gets too large. The representations of infinity and NaN are uppercased, too.
'n' Number. This is the same as 'g', except that it uses the current locale setting to insert the appropriate number separator characters.
'%' Percentage. Multiplies the number by 100 and displays in fixed ('f') format, followed by a percent sign.
None Similar to 'g', except that fixed-point notation, when used, has at least one digit past the decimal point. The default precision is as high as needed to represent the particular value. The overall effect is to match the output of str() as altered by the other format modifiers.

Some of the more interesting type identifier options are related to decimal numbers. Python makes it very easy to display decimal values in different formats with very little effort. Here is an example of a decimal number being displayed in several formats by simply modifying the type identifier of the format_spec.

x = 2378
print("decimal:     {:d}".format(x)) # decimal
print("hexadecimal: {:x}".format(x)) # hexadecimal
print("octal:       {:o}".format(x)) # octal
print("binary:      {:b}".format(x)) # binary

decimal:     2378
hexadecimal: 94a
octal:       4512
binary:      100101001010

For both strings and decimals (assuming you want a standard integer representation) it is possible to omit the type identifier, and the formatted output should appear as you would expect it to.

For floating point values this is not true. Omitting the type identifier results in the implicit selection of 'g' which makes interpretations about the best way to display the floating-point value. What I don't like about this, in addition to changing formats depending on the size of the value, is that the result does not match either representation generated by explicitly selecting the other formats. In the example below, a floating point value is displayed using general, exponential, and floating-point formats. the general type results in the output format changing when the value is either small or large, but in neither case does the value perfectly match the explicit selection of that type.

print('-'*40) # print separator
x = 24873.34223
print("general:        {}".format(x)) 
print("exponential:    {:e}".format(x)) 
print("floating-point: {:f}".format(x))
print('-'*40) # print separator
x = x * 375
print("general:        {}".format(x)) 
print("exponential:    {:e}".format(x)) 
print("floating-point: {:f}".format(x)) 

general:        24873.3
exponential:    2.487334e+04
floating-point: 24873.342230
general:        9.3275e+06
exponential:    9.327503e+06
floating-point: 9327503.336250

Even though it is possible (and common) to use blank curly braces {} and simply let Python decide the best way to display the object provided, if you desire an object to be represented in a floating point format, it is recommended to at least specify {:f}. This will provide a consistent output. Of course, for most applications, the default precision of 6 decimal places is probably a bit much. Since you've already gone through the trouble of writing {:f}, with a few more characters we can make it even better.

The [width] and [precision] options for FLOATING-POINT objects

The [precision] identifier is useful for defining how many digits appear after the decimal place in a floating-point representation. To define the precision, simply add a dot and a number like this ({:.2f}) before the floating-point type identifier. The example below shows a simple usage of precision.

x = 192.324629
print("Checking: {:.2f}".format(x))
print("Savings:  {:.2f}".format(x*9))
print("401(k):   {:.2f}".format(x*99))

Checking: 192.32
Savings:  1730.92
401(k):   19040.14

For many applications, defining the [precision] is enough. However, if there is a desire to align the decimal points despite changing sizes of the whole number part to the left of the decimal point, this can be done by specifying a [width] value. The width value defines the entire positional size of the floating point value, including the decimal point, and fractional digits. By default, the number will be right-aligned within the defined space, when the number is smaller than the space defined.

x = 192.324629
print("Checking: {:8.2f}".format(x))
print("Savings:  {:8.2f}".format(x*9))
print("401(k):   {:8.2f}".format(x*99))

Checking:   192.32
Savings:   1730.92
401(k):   19040.14

If the displayed number exceeds the size of the defined width, Python will grow the size automatically. This will mess up the nice alignment of the decimal places, but the displayed value will always be correct.

I wasn't going to discuss the [fill] identifier for floating-point representations, because it isn't normally useful, however, it can be nice when testing out displayed values to visualize the maximum size of displayed objects. In the example below, I added an asterisk (*) as the fill character, and defined an [align] identifier of > to tell format which direction to align the number in. The fill character is applied in the opposite direction. When using fill, the align identifier is required. There is one exception, which is when zero (0) is used as the fill character. In that case, right-aligned is the default.

x = 192.324629
print("Checking: {:*>8.2f}".format(x))

Checking: **192.32

In the example above, try changing the width value with the asterisk fill character, and see how it changes. Try replacing the fill character with 0. While using 0 as the fill character, delete the align identifier.

The [width] option for STRING objects

In the examples above, I simply added spaces after the "hard-coded" strings to align the position of the displayed numbers. In many cases this is easy and appropriate, but there may be situations where the string identifiers are not known before-hand, like when reading them out of a file. In that case, it may be useful to use the [width] identifier. The width defines how many minimum characters the string will occupy. If the string is smaller than the defined width, format() will extend the size of the string (using spaces by default) until the length is as large as the defined width. If the string is larger than the defined width, the space occupied will grow to equal the length of the string.

In the example below, notice that without any [width] specification, the values associated with the strings appear directly after the length of the strings (plus the 3 spaces defined by the print statement format string), which provides a haphazard presentation of values. The second set of outputs uses a defined width of 30, which is just larger than the largest string so all of the displayed numbers are properly aligned.

x_str = 'number of accidents per year:'
y_str = 'Avg. cost per accident:'
z_str = 'Avg. cost per year:'
x = 25; y = 2456.789; z = y*25
print('-'*40) # print separator
print("{:s}    {:d}".format(x_str, x))
print("{:s}   ${:.2f}".format(y_str, y))
print("{:s}   ${:.2f}".format(z_str, z))
print('-'*40) # print separator
print("{:30s} {:d}".format(x_str, x))
print("{:30s}${:.2f}".format(y_str, y))
print("{:30s}${:.2f}".format(z_str, z))

number of accidents per year:    25
Avg. cost per accident:   $2456.79
Avg. cost per year:   $61419.73
number of accidents per year:  25
Avg. cost per accident:       $2456.79
Avg. cost per year:           $61419.73

In some cases, the size of the largest string may change, and it would be desirable to modify the width. If there were hundreds of "hard-coded" width values, this could be a real pain to update. A really powerful option with format() is to be able to define named values and also have format use those values in the format string. An example is shown below, where a local variable w is set equal to the value of an external variable str_width in the format() arg list. Inside the format string curly braces are used to tell format() to replace the variable name {w} with the value. This results in a dynamic assignment of a width value. Using this technique, hundreds of width values could be modified with one line of code.

x_str = 'number of accidents per year:'
y_str = 'Avg. cost per accident:'
z_str = 'Avg. cost per year:'
x = 25; y = 2456.789; z = y*25
str_width = 30
print("{:{w}s} {:d}".format(x_str, x, w=str_width))
print("{:{w}s}${:.2f}".format(y_str, y, w=str_width))
print("{:{w}s}${:.2f}".format(z_str, z, w=str_width))

number of accidents per year:  25
Avg. cost per accident:       $2456.79
Avg. cost per year:           $61419.73

Try changing the value of the str_width and see what happens.

The [fill] and [width] options for INTEGER objects

Previously, we looked at integer values presented in different formats, as shown again below. What we didn't discuss is that the hexadecimal and binary values displayed do not convey complete information as presented. For example, let's assume we intended to display values from a 16-bit processor. In hexadecimal, the maximum value from a 2^16 processor is FFFF, and the hexadecimal value displayed should have been 094A, and not 94A to convey not only the value, but the maximum size of the processor's internal registers. The binary value is also incomplete, as the value of 100101001010 should have been 0000100101001010.

decimal:     2378
hexadecimal: 94a
octal:       4512
binary:      100101001010

The example below has been modified to include the [fill] and [width] values which convey the complete information. In this case, [fill] is specified as 0 to include preceding 0's as needed, and [width] is specified as 4 for the hexadecimal values and 16 for the binary values, to represent a 2^16 processor. The example below also used the capital X type identifier, as opposed to the lower case x to present the hexadecimal values in upper case, which I prefer. This complete representation is definitely preferred in situations where someone must transcribe such values, and might otherwise miss preceding zeros which might be required.

x = 2378
print("hexadecimal: {:04X}".format(x)) # 2^16 hexadecimal
print("binary:      {:016b}".format(x)) # 2^16 binary

hexadecimal: 094A
binary:      0000100101001010
Modifying the print() arguments

At the beginning of this page I said that there were times when it was useful to modify the arguments of the print() function to inhibit the new line terminator. Let's discuss that now. If you look at the documentation for the print() function (also shown below) you will see that there are actually 5 arguments to the print() function. All of them except for the first have defined default values. This means that if you don't assign a value, the default values will be used instead.

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

If we didn't want to have a newline (\n) placed at the end of our printed string, then we could override the end argument value with an empty string (''). This would allow us to keep adding characters to the same line using multiple print statements. One additional item however is that Python (or Windows) will try to optimize code execution by waiting to print the complete line, which it does by waiting for the newline terminator. To force each printed output to be displayed, it is necessary to override flush to True. The example below shows how this can be used to wrap a statement around a piece of code that performs a task (and usually takes a little while) to show when it started and completed.

The newline (\n) mentioned above is called an escape sequence. An escape sequence is a special character (or sequence of characters) following a backslash in a string. When Python encounters an escape sequence it takes an action instead of printing the sequence. The list of escape sequences can be found here.

print("Processing File... ", end='', flush=True)
# do some file processing stuff here

Processing File... Done

For tasks that take a very long time, adding some walking dots during the processing can help put people at ease by letting them know that the computer is still working, and isn't hung up in the weeds somewhere. The example below uses the time library to call the sleep() function each time through a for loop. This adds a small delay to make the behavior of the example more consistent with what a user would see. After the processing is completed (i.e. all 10 dots are printed), the user is informed that the processing is complete.

import time
print("Processing File", end='', flush=True)
for x in range(10):
    print(".", end='', flush=True)
print(" Done")

Processing File.......... Done

Other Printing and Formatting Options

There are many ways to accomplish almost everything in Python, and printing and string formatting is no exception. Even if you never use the other options, it is good to know how to recognize them.

First, you may have noticed that I included Python3 in the name of the first section. That was intentional. If you search the Internet for Python examples you may find several examples where it looks like the print() function is used without the parenthesis. That's a good indication that the example is for Python2. In Python2 print wasn't a function call, it was a special statement, and the arguments were simply listed after the print statement.

1. "old style" printf-like formatting with modulo (%) operator

The first style we aren't going to cover is the a formatting style that emulated the C printf formatting. In fact, it's a good guess that anything commonly listed in the references as "old style", will not be something we'll be covering. But just so that you can recognize it, there is an example of the formatting style below. The modulo (%) operator is used within the string to identify where in the string the values should be placed, and the formatting that should be used. Then a modulo (%) operator is placed after the string, and after that a list of comma separated arguments is provided within parenthesis for performing the string replacements. When using this style it is necessary that the type of object must match the identifier, for example, %s = string, %d = decimal, %f = floating-point. Formatting options can also be identified within the string, as in %0.2f, meaning only display 2 numbers after the decimal point. For more information on this style, you can go here.

name = 'Patrick'; age = 19; balance = 123.2837462
print 'My name is %s. I am %d years old.' % (name, age)
print 'I have $%0.2f in my checking account.' % (balance)

My name is Patrick. I am 19 years old.
I have $123.28 in my checking account.

Note: Python3 will still recognize this formatting style, if the parenthesis are added to change print from a keyword to a function call.

2. formatting using string literals, or f-strings

The second style we aren't going to cover is actually a new formatting style called f-strings that was introduced in Python 3.6. I may cover this in a later topic, but for now I would like to stick to the most common formatting, which is currently .format(). In the future, this may be replaced with f-strings. We'll see.

F-strings are similar in structure and formatting to the .format() that we discussed, except that the external variable name is placed directly into the replacement_field, and there is no .format() member function call. The f in the front of the f-string is a Python keyword. When f is followed by a properly formatted string, Python simply knows that it needs to return a string at that location with all of the proper replacements.

name = 'Patrick'; age = 19; balance = 123.2837462
print(f'My name is {name:s}. I am {age:d} years old.')
print(f'I have ${balance:.2f} in my checking account.')

My name is Patrick. I am 19 years old.
I have $123.28 in my checking account.

Additional Resources