8.4. Builtin Printing

8.4.1. Escape Characters

../../_images/type-machine1.jpg

Figure 8.11. Why we have '\r\n' on Windows?

Table 8.15. Frequently used escape characters

Sequence

Description

\n

New line (LF - Linefeed)

\r

Carriage Return (CR)

\t

Horizontal Tab (TAB)

\'

Single quote '

\"

Double quote "

\\

Backslash \

Table 8.16. Less frequently used escape characters

Sequence

Description

\a

Bell (BEL)

\b

Backspace (BS)

\f

New page (FF - Form Feed)

\v

Vertical Tab (VT)

\uF680

Character with 16-bit (2 bytes) hex value F680

\U0001F680

Character with 32-bit (4 bytes) hex value 0001F680

\o755

ASCII character with octal value 755

\x1F680

ASCII character with hex value 1F680

print('\U0001F680')     # 🚀

8.4.2. String Module

import string

string.punctuation
# '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

string.whitespace
# ' \t\n\r\x0b\x0c'

string.ascii_lowercase
# 'abcdefghijklmnopqrstuvwxyz'

string.ascii_uppercase
# 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

string.ascii_letters
# 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

string.digits
# '0123456789'

string.hexdigits
# '0123456789abcdefABCDEF'

string.octdigits
# '01234567'

string.printable
# '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

8.4.4. Function definition

def print(*values, sep=' ', end='\n', file=sys.stdout, flush=False):
    """
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.
    """
    ...

8.4.5. Intuitive implementation

Intuitive implementation of print function:

def print(*values, sep=' ', end='\n', ...):
    return sep.join(values) + end

8.4.6. Printing multiple values

name = 'José Jiménez'

print('My name...', name, '!')
# My name... José Jiménez!
name = 'José Jiménez'

print('My name...', name, '!', sep=';')
# My name...;José Jiménez;!

8.4.7. String concatenation

  • + operator (with side effects)

  • str.join()

  • str.format()

  • f-string formatting (preferred)

8.4.8. + Operator

  • f-string formatting are preferred over str addition

  • How many str are in the memory?

name = 'Mark Watney'

'My name... ' + name
# 'My name... José Jiménez'

+ Operator side effect:

name = 'Mark Watney'
age = 42

'My name... ' + name + ' and I am ' + str(age) + ' years old!'
# 'My name... Mark Watney and I am 42 years old!'

8.4.9. str.join()

data = ['Mark Watney', 'Melissa Lewis', 'Rick Martinez']

' '.join(data)              # 'Mark Watney Melissa Lewis Rick Martinez'
','.join(data)              # 'Mark Watney,Melissa Lewis,Rick Martinez'
', '.join(data)             # 'Mark Watney, Melissa Lewis, Rick Martinez'

8.4.10. Variable interpolation

8.4.11. Operator: %s, %d, %f

  • positional

  • keyword

  • %s - str

  • %d - int

  • %f - float

name = 'José Jiménez'
age = 42
pi = 3.141592653589793

'My name... %s' % name             # My name... José Jiménez
'My name... %d' % name             # TypeError: %d format: a number is required, not str
'My name... %f' % name             # TypeError: must be real number, not str

'I have %s years' % age             # 'I have 42 years'
'I have %d years' % age             # 'I have 42 years'
'I have %f years' % age             # 'I have 42.000000 years'

'Number PI is %s' % pi              # 'Number PI is 3.141592653589793'
'Number PI is %f' % pi              # 'Number PI is 3.141593'
'Number PI is %d' % pi              # 'Number PI is 3'
name = 'José Jiménez'
age = 42

'%s has %s years' % (name, age))      # José Jiménez has 42 years
'%s has %s years' % (age, name))      # 42 has José Jiménez years
pi = 3.141592653589793

def square(value):
    return value ** 2

'PI squared is %f' % square(pi)      # 'PI squared is 9.869604'
data = {
    'name': 'José Jiménez',
    'age': 42,
}

'%(name)s has %(age)d years' % data
# 'José Jiménez has 42 years'

'%(name)s has %(age)d years' % {'name': 'José Jiménez', 'age': 42}
# 'José Jiménez has 42 years'
name = 'José Jiménez'
age = 42

'My name... %(name)s' % locals()
# 'My name... José Jiménez'

8.4.12. str.format()

name = 'José Jiménez'
age = 42

'{} is {} years'.format(name, age)                     # 'José Jiménez is 42 years'
'{0} is {1} years'.format(name, age)                   # 'José Jiménez is 42 years'
'{1} is {0} years'.format(name, age)                   # '42 is José Jiménez years'
name = 'José Jiménez'
age = 42

'{a} is {b} years'.format(a=name, b=age)               # 'José Jiménez is 42 years'
'{name} is {age} years'.format(name=name, age=age)     # 'José Jiménez is 42 years'
'{age} is {name} years'.format(**locals())             # '42 is José Jiménez years'

8.4.13. f-strings - Python >= 3.6

  • Preferred way

name = 'José Jiménez'
pi = 3.141592653589793

def square(value):
    return value ** 2

f'My name... {name}'                      # 'My name... José Jiménez'
f'PI squared is {square(pi)}'             # 'PI squared is 9.869604401089358'
from datetime import datetime


now = datetime.now()
iso = '%Y-%m-%dT%H:%M:%SZ'

f'Today is: {now:%Y-%m-%d}')              # 'Today is: 1969-07-21'
f'Today is: {now:{iso}}')                 # 'Today is: 1969-07-21T02:56:15Z'

8.4.14. Advanced String Formatting

  • Since Python 3.0: PEP 3101 -- Advanced String Formatting

8.4.15. Basic formatting

text = 'PI'
number = 3.14

f'{text} = {number}'            # 'PI = 3.14'

8.4.16. Padding and aligning strings

text = 'hello'

f'{text:10}'                    # 'hello     '
f'{text:<10}'                   # 'hello     '
f'{text:^10}'                   # '  hello   '
f'{text:>10}'                   # '     hello'
f'{text:.<10}'                  # 'hello.....'
f'{text:_^10}'                  # '__hello___'

8.4.17. Type casting

number = 3

f'{number}'                    # '3'
f'{number:d}'                  # '3'
f'{number:f}'                  # '3.000000'
number = 3.141592653589793

f'{number}'                     # '3.141592653589793'
f'{number:d}'                   # ValueError: Unknown format code 'd' for object of type 'float'
f'{number:f}'                   # '3.141593'
text = 'hello'

f'{text}'                       # 'hello'
f'{text:d}'                     # ValueError: Unknown format code 'd' for object of type 'str'
f'{text:f}'                     # ValueError: Unknown format code 'f' for object of type 'str'
f'{14:#b}'                      # '0b1110'
f'{14:b}'                       # '1110'
f'{10:#o}'                      # '0o12'
f'{10:o}'                       # '12'
f'{255:#x}'                     # '0xff'
f'{255:x}'                      # 'ff'
f'{255:X}'                      # 'FF'

8.4.18. Truncating and rounding

text = 'Lorem Ipsum'

f'{text:.5}'                    # 'Lorem'
f'{text:10.5}'                  # 'Lorem     '
number = 3.141592653589793

f'{number:.2f}'                 # '3.14'
f'{number: 6.2f}'               # '  3.14'
f'{number:06.2f}'               # '003.14'
f'{number:.6.2f}'               # ValueError: Invalid format specifier

8.4.19. Signed numbers

positive = 42
negative = -42


f'{positive:d}'                 # '42'
f'{negative:d}'                 # '-42'

f'{positive: d}'                # ' 42'
f'{negative: d}'                # '-42'

f'{positive:+d}'                # '+42'
f'{negative:+d}'                # '-42'

f'{negative:=5d}'               # '-  42'
f'{positive:=+5d}'              # '+  42'

8.4.20. Get from dict

data = {
    'firstname': 'Mark',
    'lastname': 'Watney'
}

f'{data["firstname"]}'         # 'Mark'
f'{data["lastname"]}'          # 'Watney'

8.4.21. Get from sequence

data = ['a', 'b', 'c']

f'{data[1]}'                    # 'b'
f'{data[0]} -> {data[2]}'       # 'a -> c'
data = ('a', 'b', 'c')

f'{data[1]}'                    # 'b'
f'{data[0]} -> {data[2]}'       # 'a -> c'
data = {'a', 'b', 'c'}

f'{data[1]}'
# Traceback (most recent call last):
# TypeError: 'set' object is not subscriptable

8.4.22. Get from class

class Iris:
    species = 'setosa'
    measurements = {
        'sepal_length': 5.1,
        'sepal_width': 3.5,
        'petal_length': 1.3,
        'petal_width': 0.4,
    }

flower = Iris()

f'{flower.species}'                             # 'setosa'
f'{flower.species:.3}'                          # 'set'
f'{flower.measurements["sepal_width"]}'         # '3.5'
f'{flower.measurements["sepal_width"]:.3f}'     # '3.500'

8.4.23. Parametrized formats

text = 'hello'

align = '^'
width = 10


f'{text:{align}}'               # 'hello'
f'{text:{align}{width}}'        # '  hello   '
number = 3.14159

align = '>'
width = 10
precision = 2
sign = '+'


f'{number:.{precision}f}'                       # '3.14'
f'{number:{width}.{precision}f}'                # '      3.14'
f'{number:{align}{sign}{width}.{precision}f}'   # '     +3.14'

8.4.24. Datetime

from datetime import datetime


now = datetime(1969, 7, 21, 2, 56, 15)

iso = '%Y-%m-%dT%H:%M:%SZ'
date = '%Y-%m-%d'
time = '%H:%M'


f'{now:%Y-%m-%d %H:%M}'       # '1969-07-21 02:56'

f'{now:{iso}}'                # '1969-07-21T02:56:15Z'
f'{now:{date}}'               # '1969-07-21'
f'{now:{time}}'               # '02:56'

8.4.25. Custom object formatting

class Point:
    def __init__(self, x, y, z=0):
        self.x = x
        self.y = y
        self.z = z

    def __format__(self, format):

        if format == '2D':
            return f"({self.x}, {self.y})"

        elif format == '3D':
            return f"({self.x}, {self.y}, {self.z})"

        elif format == 'dict':
            return str(self.__dict__)

        elif format == 'tuple':
            return str(tuple(self.__dict__.values()))

        elif format == 'json':
            import json
            return json.dumps(self.__dict__)

        else:
            raise ValueError


point = Point(x=1, y=2)

f'{point:2D}'           # '(1, 2)'
f'{point:3D}'           # '(1, 2, 0)'
f'{point:tuple}'        # '(1, 2, 0)'
f'{point:dict}'         # "{'x': 1, 'y': 2, 'z': 0}"
f'{point:json}'         # '{"x": 1, "y": 2, "z": 0}'

8.4.26. str and repr

  • !s executes __str__()

  • !r executes __repr__()

class Point:
    def __init__(self, x, y, z=0):
        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return f'({self.x}, {self.y}, {self.z})'

    def __repr__(self):
        return f'Point(x={self.x}, y={self.y}, z={self.z})'


point = Point(x=1, y=2)

f'{point!s}'            # '(1, 2, 0)'
f'{point!r}'            # 'Point(x=1, y=2, z=0)'

8.4.27. Quick and easy debugging

number = 3

f'{number*9 + 15=}'
# x*9 + 15=42
astronaut = 'Watney'
birthdate = date(1975, 7, 31)
delta = date.today() - member_since

f'{user=} {member_since=}'
# "astronaut='Watney' birthdate=datetime.date(1975, 7, 31)"

f'{astronaut=!s}  {delta.days=:,d}'
# 'astronaut=Watney  delta.days=16,075'
print(f'{theta=}  {cos(radians(theta))=:.3f}')
# theta=30  cos(radians(theta))=0.866

8.4.28. pprint

from pprint import pprint

data = [
    {'firstname': 'Mark', 'lastname': 'Watney'},
    {'firstname': 'Melissa', 'lastname': 'Lewis'},
    {'firstname': 'Rick', 'lastname': 'Martinez'},
]

pprint(data)
# [{'firstname': 'Mark', 'lastname': 'Watney'},
#  {'firstname': 'Melissa', 'lastname': 'Lewis'},
#  {'firstname': 'Rick', 'lastname': 'Martinez'}]
from pprint import pformat

data = [
    {'firstname': 'Mark', 'lastname': 'Watney'},
    {'firstname': 'Melissa', 'lastname': 'Lewis'},
    {'firstname': 'Rick', 'lastname': 'Martinez'},
]

# returns formatted data
my_string = pformat(data)

8.4.29. Use Case - 0x01

>>> 
... from time import sleep
...
...
... def progressbar(percent):
...     filled = '=' * percent
...     empty = ' ' * (100-percent)
...     clear = '\b' * 110
...     bar = f'{clear}{percent:4}% |{filled}{empty}|'
...     print(bar, end='')
...
...
... for i in range(0,101):
...     progressbar(i)
...     sleep(0.2)

8.4.30. Assignments

8.4.30.1. Powielanie napisów

  • Assignment: Powielanie napisów

  • Complexity: easy

  • Lines of code: 8 lines

  • Time: 5 min

English:
  1. Given string: text = 'Lorem Ipsum'

  2. Write three functions:

    1. print_1(text) using range()

    2. print_2(text) using while loop

    3. print_3(text) using string multiplication

  3. Each function should print 5 copies of this string

  4. Each string in a separate line

  5. Write doctest for all functions

  6. Run doctests - all must succeed

Polish:
  1. Dany jest ciąg znaków: text = 'Lorem Ipsum'

  2. Napisz trzy funkcje:

    1. print_1(text) wykorzystującą range()

    2. print_2(text) wykorzystującą pętlę while

    3. print_3(text) wykorzystującą mnożenie stringów

  3. Każda funkcja ma wyświetlić 5 kopii tego ciągu znaków

  4. Każdy ciąg znaków w osobnej linii

  5. Napisz doctest do wszystkich funkcji

  6. Uruchom doctesty - wszystkie muszą się powieść

8.4.30.2. Przeliczanie temperatury

  • Assignment: Przeliczanie temperatury

  • Complexity: easy

  • Lines of code: 8 lines

  • Time: 13 min

English:
  1. Write a program that will display a table of Celsius to Fahrenheit conversions in the range from -20 to +40 degrees Celsius (every 5 degrees).

  2. The result must be as shown in the listing below

  3. The sign must always be displayed

  4. Pay attention to the text justification

  5. Pay attention to filling the space not occupied by numbers

  6. Run doctests - all must succeed

Polish:
  1. Napisz program, który wyświetli tabelę przeliczeń stopni Celsjusza na stopnie Fahrenheita w zakresie od –20 do +40 stopni Celsjusza (co 5 stopni).

  2. Wynik musi być taki jak na listingu poniżej

  3. Znak ma być zawsze wyświetlany

  4. Zwróć uwagę na wyjustowanie tekstu

  5. Zwróć uwagę na wypełnienie miejsca niezajętego przez cyfry

  6. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0
-------------------------------------------
| Temperature | -     20°C | ....-4....°F |
-------------------------------------------
| Temperature | -     15°C | ....+5....°F |
-------------------------------------------
| Temperature | -     10°C | ...+14....°F |
-------------------------------------------
| Temperature | -      5°C | ...+23....°F |
-------------------------------------------
| Temperature | +      0°C | ...+32....°F |
-------------------------------------------
| Temperature | +      5°C | ...+41....°F |
-------------------------------------------
| Temperature | +     10°C | ...+50....°F |
-------------------------------------------
| Temperature | +     15°C | ...+59....°F |
-------------------------------------------
| Temperature | +     20°C | ...+68....°F |
-------------------------------------------
| Temperature | +     25°C | ...+77....°F |
-------------------------------------------
| Temperature | +     30°C | ...+86....°F |
-------------------------------------------
| Temperature | +     35°C | ...+95....°F |
-------------------------------------------
| Temperature | +     40°C | ...+104...°F |
Hints:
  • Fahrenheit to Celsius: (°F - 32) / 1.8 = °C

  • Celsius to Fahrenheit: (°C * 1.8) + 32 = °F

  • def celsius_to_fahrenheit(degree):
        return degree*1.8 + 32