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:
>>> 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
>>> 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.
>>> 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.
>>> 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
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
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, ...