Python tip of the week 6, String formatting

This week’s tip is about ways to format strings in Python.

An ‘intuitive’ way for string formatting can be using ‘+’ operator for concatenating strings. To use this formatting style, we have to cast the integers or other data types into strings manually. An example can be as following:

>>> num_of_items = 42
>>> type_of_items = 'songs'
>>> print('Here, we have ' + str(num_of_items) + ' ' + type_of_items)

Output: Here, we have 42 songs

While the ‘+’ operator seems easy to use, it comes with a bunch of problems (e.g. manual type casting, difficulty in reading code). Instead, you should consider using one of these recommended methods:

Below, you can find some examples of use:

Basic formatting

>>> num_of_items = 42
>>> type_of_items = 'songs'

# Old style
>>> print('Here, we have %d %s' % (num_of_items, type_of_items))

# New style
>>> print('Here, we have {} {}'.format(num_of_items, type_of_items))

# F-strings
>>> print(f'Here, we have {num_of_items} {type_of_items}')

Output: Here, we have 42 songs

Named placeholders

>>> params = {'hop_size': 512, 'frame_size': 1024}

# Old style
>>> print('The parameters for hop size is %(hop_size)d, and frame size is %(frame_size)s.' % params)

# New style
>>> print('The parameters for hop size is {hop_size}, and frame size is {frame_size}.'.format(**params))

# F-strings
>>> print(f'The parameters for hop size is {params["hop_size"]}, and frame size is {params["frame_size"]}.')

Output: The parameters for hop size is 512, and frame size is 1024.

Number formatting

>>> def get_duration():
>>>     return 331.1932148

# Old Style
>>> print('Duration of this track is %.2f seconds.' % get_duration())

# New Style
>>> print('Duration of this track is {:.2f} seconds'.format(get_duration()))

# F-strings
>>> print(f'Duration of this track is {get_duration():.2f} seconds.')

Output: Duration of this track is 331.19 seconds.

Padding and alignment

>>> songs = ['Yesterday', 'All You Need Is Love', 'Hey Jude']
>>> albums = ['Help!', 'Magical Mystery Tour', 'The Beatles']
>>> song_ids = [483, 65448, 98]

# Old Style
>>> for i in range(3):
>>>     print('%20s - %-20s - %6d ' % (songs[i], albums[i], song_ids[i]))

# New Style
>>> for i in range(3):
>>>     print('{:>20} - {:<20} - {:>6}'.format(songs[i], albums[i], song_ids[i]))

# F-strings
>>> for i in range(3):
>>>    print(f'{songs[i]:>20} - {albums[i]:<20} - {song_ids[i]:>6}')

Output:            Yesterday - Help!                -    483
        All You Need Is Love - Magical Mystery Tour -  65448
                    Hey Jude - The Beatles          -     98

Example for Template strings

In most of the cases, one of the methods shown above would be appropriated. However, these methods might introduce security vulnerabilities to your programs. For instance, a user of a web application could retrieve some variables with a designed input. Let’s have a look at a simple example where an hypothetical attacker would be able to access some global variables:

# This is a variable we don't want to show
>>> SECRET_VARIABLE = "don't tell anyone"

>>> class Music:
>>>     def __init__(self):
>>>         pass

>>> song = Music()

# New Style
>>> user_provided_string = '{track.__init__.__globals__[SECRET_VARIABLE]}'
>>> print(user_provided_string.format(track=song))

Output: don't tell anyone

Here is where the Template strings method becomes useful:

>>> from string import Template

>>> user_provided_string = '${track.__init__.__globals__[SECRET_VARIABLE]}'
>>> print(Template(user_provided_string).substitute(track=song))

Raises: ValueError: Invalid placeholder in string: line 1, col 1

Bonus tip: join function

For joining all the items in a tuple or a list into a single string, you might be tented to use a for loop and concatenate elements one by one. However, the ‘.join()’ function provides a better way to do it:

>>> songs = ['Yesterday', 'Come Together', 'Hey Jude', 'Blackbird', '...']
>>> message = 'Processing the songs: '
>>> message += ', '.join(songs)
>>> print(message)

Output: Processing the songs: Yesterday, Come Together, Hey Jude, Blackbird, ...