Printing and Formatting

Indices and tables

Printing

Strings, and things that can automatically be converted to a string (like int), are printed to a text stream using the print() method, which has the following signature:

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

By default, any output gets sent to stdout. However, print statements can also direct their output to stderr, or, any text file opened using the open() function.

When printing, the stream may be buffered. To flush the buffer at any given time, set the flush keyword argument to True.

The print() function allows multiple, separate strings to be output at once, each demarkated using the string given by sep. After all strings are output, print() inserts the end string to finish it off. For example:

With default sep and end, there is a space between strings and a newline at the end:

>>> print('foo', 'bar', 'baz')
foo bar baz

The sep string can be changed to alter the intra-string space:

>>> print('foo', 'bar', 'baz', sep='-')
foo-bar-baz
>>> print('foo', 'bar', 'baz', sep='')
foobarbaz

The end string can be changed to alter the end of the output:

>>> print('foo', 'bar', 'baz', end='#')
foo bar baz#

Setting end to be an empty string is a good way to prevent newlines between multiple print() statements. Consider the following examples:

>>> print('foo'); print('bar'); print('baz')
foo
bar
baz
>>> print('foo', end=''); print('bar', end=''); print('baz', end='')
foobarbaz

Python can output 2 types of strings:

  1. The “informal” string, which is nicely formatted and meant to be easily understandable by users. This format cannot be passed to the eval() function to recreate the object. This is the format the print() function attempts to always produce by default (under the hood, it calls the objects __str__() method). For example:
>>> import datetime
>>> alarm = datetime.time(7, 30, 00)
>>> print(alarm)
07:30:00
  1. The “formal” string, which is not necessarily nicely formatted, but, it can be passed to the eval() function to recreate the object. This format is also called the, representational or reproducing format, and is obtained by calling the objects __repr__() method. For example:
>>> import datetime
>>> alarm = datetime.time(7, 30, 00)
>>> alarm.__repr__()
'datetime.time(7, 30)'
>>> alarm2 = eval('datetime.time(7, 30)')
>>> type(alarm2)
datetime.time
>>> print(alarm2)
07:30:00

Not all objects can produce a reproducing output. If the there is no reproducing output available, an informational string is output between < > brackets instead. For example:

>>> a = object()  # As instance of the base class of all objects
>>> a.__repr__()
'<object object at 0x7f6ab24d7590>'

Try it!

  • Create two, multi-line strings.
  • Print the multi-line strings created above using one print statement such that the strings are separated by “::”, and a single “%” is printed at the end of the printed text.

Formatting

In contrast to the Zen of Python (PEP 20) idea that there should be one obvious way to do something, Python supports several methods for formatting strings for display.

C-Style printf

This is the “old” style that uses the % operator (a.k.a. the string formatting or interpolation operator) and resembles the printf support in C. It works as follows:

  • Embed % in string to indicate a replacement field
  • After % in string, place a conversion type character
  • After string is closed, place a %, followed by a single non-tuple, a tuple or a dict.

For example:

>>> print("ERROR NUM: %d  MSG: %s" % (5, 'Engine shutdown late.'))
ERROR NUM: 5  MSG: Engine shutdown late.

There are many conversion types that are supported as well as flags for padding and justification. However, this style is deprecated in favor of other methods (it doesn’t work well with tuples and dicts) so it won’t be discussed further.

Template Strings

Template strings provide a simple substitution method that uses the $ character instead of %. It works as follows:

  • Import Template from the string standard library package

  • Construct the template string that needs fields replaced at runtime using:

    • $$ is an escape to get a literal $
    • $identifier as a placeholder for a value that needs to be substituted in when expanded.
  • Call substitute() on the template to expand the embedded identifiers and fill them with values passed into the substitute() method.

For example:

>>> from string import Template
>>> t = Template('Hello $thing')
>>> t.substitute(thing="World")
'Hello World'
>>> t.substitute(thing="Students")
'Hello Students'

The upside to Template strings is that they allow you to accept formatting strings from users at run-time. Additionally, due to their simplistic nature they can only access values passed in to the substitute() method which prevents malicious format strings from accessing data in your program that should be off limits.

The downside to template strings is you can’t include any additional format conversion or alignment flags in the template like you can with the other formatting methods. If required, you must pre-format the data prior to substituting in the final values.

Format Strings

Format strings are the “new” style (defined in PEP 3101) used to format strings. There are 2 ways to access this machinery

  • the built-in function format(), or,
  • the format() method on instances of the string class.

Both use the same implementation under the hood.

A format string contains 2 types of elements:

  • Literal text, which is copied verbatim to the output.
  • Replacement fields, which have formatted text substituted in.

A replacement field is surrounded by {} braces and specifies 2 things:

  • What to print
  • How to format the thing being printed.

A full description of replacement fields is provided here. In short, a replacement field can be represented by the production shown below:

replacement_field ::=  “{” [field_name] [“!” conversion] [“:” format_spec] “}”
field_name        ::=  arg_name (“.” attribute_name | “[” element_index “]”)*
arg_name          ::=  [identifier | integer]
attribute_name    ::=  identifier
element_index     ::=  integer | index_string

In it:

  • field_name is the “what to print”, and,
  • format_spec is the “how to format the thing being printed”.

Let’s run though some examples of how to choose what to print. There are several options:

By position using the order of arguments, as in:

>>> print("The {} and the {}".format("foo", "bar", "baz"))
The foo and the bar

By position using specific arguments by number, as in:

>>> print("The {0} and the {1}".format("foo", "bar", "baz"))
The foo and the bar
>>> print("The {0} and the {2}".format("foo", "bar", "baz"))
The foo and the baz

By referring to arguments by name, as in:

>>> print("The {a} and the {b}".format(a="foo", b="bar", c="baz"))
The foo and the bar
>>> print("The {c} and the {a}".format(a="foo", b="bar", c="baz"))
The baz and the foo

If you have an argument that is a sequence, you can pull out elements by index, as in:

>>> vals = ('foo', 'bar', 'baz')
>>> print("The {0[0]} and the {0[1]}".format(vals))
The foo and the bar
>>> print("The {0[0]} and the {0[2]}".format(vals))
The foo and the baz

You can do the same thing with a mapping, as in;

>>> vals = {'a' : 'foo',
            'b' : 'bar',
            'c' : 'baz'}
>>> print("The {0[a]} and the {0[b]}".format(vals))
The foo and the bar
>>> print("The {0[a]} and the {0[c]}".format(vals))
The foo and the baz

Note

When specifying a string index to a mapping in a replacement field, you don’t use quotes.

And of course, for either a sequence or a mapping, you can give the argument a name like we did earlier, as in:

>>> vals = ('foo', 'bar', 'baz')
>>> print("The {v[0]} and the {v[1]}".format(v=vals))
The foo and the bar
>>> vals = {'a' : 'foo',
            'b' : 'bar',
            'c' : 'baz'}
>>> print("The {v[a]} and the {v[b]}".format(v=vals))
The foo and the bar

This isn’t an exhaustive set of examples of what you can do. Reading the official documentation on the Python website will serve you well in the long term.

Once you have determined what you want to print, you have the option of deciding how you want it printed. This choice relates to the format_spec portion of the replacement field and there is an entire format specification language which you can (and should) read about here.

In short, a format_spec, which is a string of characters, can be represented by the production shown below;

format_spec ::=   [[fill]align][sign][#][0][width][,][.precision][type]

A summary of what you can do using each field is shown in Fig. 29:

../../_images/format_spec_fields.png

Fig. 29 Format Specification Fields

Note

Don’t forget that the format_spec always comes after the :.

Let’s run through some examples.

Padding and alignment with a minimum field width:

>>> print("The {:-<8} and the {:-^8} and the {:->8}".format("foo", "bar", "baz"))
The foo----- and the --bar--- and the -----baz

Using the minimum and maximum field width:

>>> print("The {:.5} {:.5}".format("fookoozoo"))
The fooko

Showing the sign of a number:

>>> print("A={:+} B={:-} C={: }".format(-1, -1, -1))
A=-1 B=-1 C=-1
>>> print("A={:+} B={:-} C={: }".format(1, 1, 1))
A=+1 B=1 C= 1

Format conversion of integers to different bases:

>>> print("{0} {0:b} {0:o} {0:x} {0:X}".format(95))
95 1011111 137 5f 5F
>>> print("{0} {0:#b} {0:#o} {0:#x} {0:#X".format(95))
95 0b1011111 0o137 0x5f 0X5F

Format conversion of integers to their character equivalent:

>>> print("{0} {0:c}".format(95))
95 _

Format conversion of floats to scientific exponent notation:

>>> print("{0} {0:e} {0:E}".format(95.62524895212))
95.62524895212 9.562525e+01 9.562525E+01

Format conversion of floats to fixed point notation with a precision given by the precision field (defaults to 6 digits):

>>> print("{0} {0:f} {0:F}".format(95.62524895212))
95.62524895212 95.625249 95.625249
>>> print("{0} {0:.8f} {0:.8F}".format(95.62524895212))
95.62524895212 95.62524895 95.62524895

Format conversion of floats to the general format which rounds to number of significant digits given by the precision field (defaults to 6 digits):

>>> print("{0} {0:g} {0:G}".format(95.62524895212))
95.62524895212 95.6252 95.6252
>>> print("{0} {0:.8g} {0:.8G}".format(95.62524895212))
95.62524895212 95.625249 95.625249

Format conversion of floats to percentage:

>>> print("{0} {0:%}".format(95.62524895212))
95.62524895212 9562.524895%

The built-in format() is good for formatting single values, as in:

>>> foo = format(255, ':>5x')
>>> foo
':::ff'

Similar to the discussion around field_name, this isn’t an exhaustive set of examples of how you can format the output. Reading the official documentation on the Python website will serve you well in the long term.

F-strings

While this course only covers what is available in Python 3.5, there is yet another way to generate strings that became available in Python v3.6. It is powerful and exciting new feature that deserves a brief mention even if they can’t be used in this course.

F-strings, or Formatted String Literals, supports the same syntax as str.format() but they also allow you to do embed expressions inside string literals. The replacement fields are expressions that are evaluated at run-time (thus the string literal is not actually a constant) and the results are then formatted using format().

To create an f-string literal, you just prepend the string with ‘f’, as in:

>>> f'Hello World'

Here is an example from PEP 498 that gives an example of its use:

>>> import datetime
>>> name = 'Fred'
>>> age = 50
>>> anniversary = datetime.date(1991, 10, 12)
>>> f'My name is {name}, my age next year is {age+1}, my anniversary is {anniversary:%A,
    %B %d, %Y}.'
'My name is Fred, my age next year is 51, my anniversary is Saturday, October 12, 1991.'
>>> f'He said his name is {name!r}.'
"He said his name is 'Fred'."

Try it!

Since C-Style strings are deprecated, template strings have limited use and F-strings are only available in Python 3.6, this Try-It section will focus specifically on Format Strings.

Given the integer value, 1234, use formatting to print it in the following ways:

  • As a hexadecimal value with the 0x prefix, right justified in a column 10 characters wide.
  • As a decimal value, centered in a column 10 characters wide, with “*“‘s around it.
  • As a binary value