String Formatting

Formatting

There are two (old one new) styles of string formatting in Python. They are very similar when dealing with simple stuff but also have a lot of in-deep presentation and text transformation options

  1. printf-style (%, old) - based on C printf style formatting that handles a narrower range of types and is slightly harder to use correctly, but is often faster for the cases it can handle.

  2. str.format() (new) provides a large degree of flexibility and customization.

  3. f-strings (Python 3.6) - inline formatting allowing to insert variables by names with format similar to format()

🪄 Code:

print_me = 100500.12
print(  "1. %s" % print_me )         # Old format, %
print(  "2. {}".format(print_me) )   # New format, format()
print( f"3. {print_me}" )            # f-strings

📟 Output:

1. 100500.12
2. 100500.12
3. 100500.12

% (printf-style formatting)

format % values

format is a string, % conversion specifications in format are replaced with zero or more elements of values.

A conversion specifier contains two or more characters and has the following components, which must occur in this order:

1. The '%' character - the start of the specifier.

2. Mapping key (optional), key name in parentheses (for example: (somename)).

3. Conversion flags (optional), which affect the result of some conversion types.

4. Minimum field width (optional). If specified as an '*' (asterisk), the actual width is read from the next element of the tuple in values, and the object to convert comes after the minimum field width and optional precision.

5. Precision (optional), given as a '.' (dot) followed by the precision. If specified as '*' (an asterisk), the actual precision is read from the next element of the tuple in values, and the value to convert comes after the precision.

6. Length modifier (optional).

7. Conversion type.

🪄 Code:

food = "Ceasar salad"
print("For breakfast today is %s" % food)

📟 Output:

For breakfast today is Ceasar salad

🪄 Code:

food = "Pizza Pepperoni and burger"
money = 130.23
print( "For lunch we have: %35s, budget: %012.3f UAH" % (food, money) )

📟 Output:

For lunch we have:          Pizza Pepperoni and burger, budget: 00000130.230 UAH

🪄 Code:

data = {"food": "Pasta Carbonara", "money": 300}
print( "And for dinner:  %(food)20s, money to spend: %(money)9.2f UAH" % data )

📟 Output:

And for dinner:       Pasta Carbonara, money to spend:    300.00 UAH

{} / format()

It's better to use this for something that requries more complex formatting

Format strings contain “replacement fields” surrounded by curly braces {}. Anything that is not contained in braces is considered literal text, which is copied unchanged to the output. If you need to include a brace character in the literal text, it can be escaped by doubling: {{ and }}.

🪄 Code:

print("Our food today is {}".format(food))

📟 Output:

Our food today is Pizza Pepperoni and burger

Several arguments:

🪄 Code:

print("Food: '{}', money: {} UAH".format(food, money))

📟 Output:

Food: 'Pizza Pepperoni and burger', money: 130.23 UAH

It is possible when using new format (format()) to specify positions:

🪄 Code:

print("Food is {1}, money: {0}, (I have exactly ${0} in my wallet!)".format(money, food))

📟 Output:

Food is Pizza Pepperoni and burger, money: 130.23, (I have exactly $130.23 in my wallet!)

It is possible to pass arguments by names:

data = {"money": 45, "food":"salad"}
# `**` - means we are passing dict as a sequence of key-value pairs
print("Want {food} but ${money} to spend".format(**data))
# print("Want {food} but ${money} to spend".format(money=45, food="salad"))

📟 Output:

Want salad but $45 to spend

More examples:

🪄 Code:

"First thing in the morning is {0}".format("coffee") # References first positional argument

📟 Output:

'First thing in the morning is coffee'

🪄 Code:

"I'd like a {} with coffee".format("cookie") # Implicitly references the first positional argument

📟 Output:

"I'd like a cookie with coffee"

🪄 Code:

"Bring me {} and {}".format("coffee", "cookie") # Same as "From {0} to {1}"

📟 Output:

'Bring me coffee and cookie'

🪄 Code:

"Remember my name: {name}".format(name="Heisenberg") # References keyword argument 'name'

📟 Output:

'Remember my name: Heisenberg'

🪄 Code:

data = dict(do_what="Remember", my_what="name", name="Heisenberg")
"{do_what} my {my_what}: {name}".format(**data)

📟 Output:

'Remember my name: Heisenberg'

🪄 Code:

"String has this method: {0.isalpha}".format("")  # 'isalpha' attribute of first positional arg

📟 Output:

'String has this method: <built-in method isalpha of str object at 0x7efc5848c030>'

Differences between formatters:

In last example assuming p is the instance of Plant class defined like:

class Plant(object):
    type = 'tree

.format() cheatsheet

.format() "cheats"

  • Show the same string several times

🪄 Code:

print('''{0}!
{1}. 
"{0}!" 
{1}. 
"What's gone with that boy, I wonder? You {0}!" {1}. 
'''.format('TOM', 'No answer'))

📟 Output:

TOM!
No answer. 
"TOM!" 
No answer. 
"What's gone with that boy, I wonder? You TOM!" No answer.
  • Convert Values to different Bases

    • You can use the following letters to convert a number to their bases:

      • decimal, hex, octal, binary

🪄 Code:

print ("{0:d} - {0:x} - {0:o} - {0:b}".format(21))

📟 Output:

21 - 15 - 25 - 10101
  • Escaping braces:

🪄 Code:

print ( "{{0}} / {}".format("TEST") )

📟 Output:

{0} / TEST

f-strings formatting

New feature appeared in Python 3.6. It is possible to inject local variable right into string (variable interpolation)

Formatted string literals are prefixed with 'f' and are similar to the format strings accepted by str.format(). They contain replacement fields surrounded by curly braces. The replacement fields are expressions, which are evaluated at run time, and then formatted using the format() protocol

This feature is described by PEP 498

The format is:

f'<text> { <expression> <optional !s, !r, or !a> <optional : format specifier> } <text> ... '

🪄 Code:

var = 34.125
print(f'{var}')
print(f'{var:012.3f}')
print(f'{var:_^12.3f}')
print(f'{var:^10}')

📟 Output:

34.125
00000034.125
___34.125___
  34.125

In case interpolating var is not defined - you'll get regular NameError:

🪄 Code:

f'{unexistent_var}'

📟 Output:

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

Input In [372], in <cell line: 1>()
----> 1 f'{unexistent_var}'


NameError: name 'unexistent_var' is not defined

🪄 Code:

f'{1/0}'

📟 Output:

---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

Input In [373], in <cell line: 1>()
----> 1 f'{1/0}'


ZeroDivisionError: division by zero

f-string are evaluated only during creation (once):

🪄 Code:

value = 100500
str_ = f'Value is {value}'

print(str_)

📟 Output:

Value is 100500

🪄 Code:

value = 42
print(str_)

📟 Output:

Value is 100500

Expressions may be evaluated directly inside a string:

🪄 Code:

f'{ 1 + 2 }'

📟 Output:

'3'

🪄 Code:

f'{"just another string"}'
'quoted string'

📟 Output:

'quoted string'

🪄 Code:

d = {"a": 100500}
l = [1, 2, 3, 4, 5]

f'{d["a"], l[2:5]}'

📟 Output:

'(100500, [3, 4, 5])'

🪄 Code:

def foo(x): 
    return "Hello! %s" % x
print(f'Result of function is:\n{foo("John")}')

📟 Output:

Result of function is:
Hello! John

Format int value as hex:

🪄 Code:

value = 1234
f'input={value:#x}'

📟 Output:

'input=0x4d2'

Format datetime objects (see docs for datetime formatting:

🪄 Code:

import datetime
now = datetime.datetime.now()
print(now.strftime("%a %d/%m/%Y"))
print(f'It was: {now:%a %d/%m/%Y}')

📟 Output:

Wed 31/08/2022
It was: Wed 31/08/2022

Dynamic width

🪄 Code:

import decimal

width = 12
precision = 5
value = decimal.Decimal('12.34567')
f'result: {value:{width}.{precision}f}'

📟 Output:

'result:     12.34567'

Templates

A bit underrated feature of builtin string module. Template is very simple template engine.

🪄 Code:

from string import Template 
s = Template('$who likes $what')
print(s.substitute(who='Johnny', what='whiskey'))

d = dict(who='tim')
#Template('Give $who $cookie').substitute(d) <--- will raise KeyError as 'cookie' not in a dictionary
print(Template('$who likes $cookies').safe_substitute(d)) # proper way

📟 Output:

Johnny likes whiskey
tim likes $cookies

Last updated