8.6 str.format() and Format Spec Mini-Language
The str.format() method and its accompanying Format Specification Mini-Language represent a powerful and flexible system for constructing strings in Python. Introduced in Python 2.6, it was designed to overcome the limitations of the older %-formatting, offering more control, better readability for complex formatting, and extensibility. It operates by replacing replacement fields, enclosed in curly braces {}, within a string with formatted values provided as arguments to the method.
Basic Syntax and Replacement Fields
The simplest form uses positional arguments. The replacement fields are replaced by the arguments in the order they are provided.
name = "Alice"
age = 30
greeting = "Hello, {}. You are {} years old.".format(name, age)
print(greeting) # Output: Hello, Alice. You are 30 years old.
You can also use keyword arguments for much clearer and more maintainable code, especially when formatting many values.
template = "Coordinates: {latitude}, {longitude}"
result = template.format(latitude="37.7749", longitude="-122.4194")
print(result) # Output: Coordinates: 37.7749, -122.4194
For maximum control and to avoid repetition, you can reference arguments by their index. This is crucial when you need to use the same value multiple times within a single format string.
data = ["Python", "Guido van Rossum"]
sentence = "The first {0[0]} conference was in {1}. {0[0]} was created by {0[1]}.".format(data, 1994)
print(sentence)
# Output: The first Python conference was in 1994. Python was created by Guido van Rossum.
The Format Specification Mini-Language
The true power of str.format() is unlocked through the Format Specification Mini-Language, specified after a colon (:) inside the replacement field. This mini-language allows you to control the precise presentation of the value, including alignment, padding, sign handling, and type conversion.
Aligning and Padding Output
You can control the width of a field and align its content (left <, right >, center ^). The fill character (a single character before the align specifier) defaults to a space.
# Right-align a string in a field of width 10
print("'{:>10}'".format("test")) # Output: ' test'
# Center an integer and fill extra space with dashes
print("'{:-^10d}'".format(42)) # Output: '----42----'
# Left-align a float and fill with dots
print("'{:.<15}'".format("Hello")) # Output: 'Hello..........'
Numeric Formatting: Integers, Floats, and Precision
For numbers, you can control the presentation of the sign, use a thousands separator, and specify precision for floating-point numbers.
# Always show the sign
print("{:+d} {:+d}".format(42, -42)) # Output: +42 -42
# Add a thousands separator
print("{:,}".format(1234567890)) # Output: 1,234,567,890
# Format a float to 2 decimal places
print("{:.2f}".format(3.14159)) # Output: 3.14
# Format as a percentage (multiplies by 100 and adds %)
print("{:.1%}".format(0.75)) # Output: 75.0%
Type Conversion within the Format Spec
You can force a value to be presented as a different type using the type specifier. This is a conversion for display purposes only; the original variable is unchanged.
num = 42
# Display as hexadecimal, binary, and character
print("int: {0:d} | hex: {0:#x} | bin: {0:b} | char: {0:c}".format(num))
# Output: int: 42 | hex: 0x2a | bin: 101010 | char: *
Accessing Attributes and Dictionary Keys
str.format() can directly access attributes of objects and keys of dictionaries, making it highly expressive. This is done using a dot (.) within the replacement field.
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
player = Player("Bob", 8500)
# Accessing an attribute
print("Player: {p.name} | Score: {p.score}".format(p=player))
# Output: Player: Bob | Score: 8500
# Accessing a dictionary key
data = {'name': 'Bob', 'score': 8500}
print("Player: {d[name]} | Score: {d[score]}".format(d=data))
# Output: Player: Bob | Score: 8500
Common Pitfalls and Best Practices
A common pitfall involves mismatched arguments. If you provide fewer arguments than there are replacement fields, you will get an IndexError. If you use keyword arguments but reference a non-existent key, you get a KeyError.
# This will raise an IndexError: Replacement index 1 out of range
# "{}{}".format("one")
# This will raise a KeyError: 'missing_key'
# "{missing_key}".format(provided_key="value")
Another subtle issue arises when trying to use curly braces literally in a string that also contains replacement fields. You must escape them by doubling them {{ and }}.
# To output "The value is {42}", you must write:
print("The value is {{{}}}".format(42))
The best practice is to use keyword arguments for most non-trivial formatting. It makes the template string self-documenting and much more resistant to errors if the order of arguments needs to change later. While str.format() is incredibly powerful, for Python 3.6+, f-strings offer a more concise and often more readable syntax for many of the same tasks. However, str.format() remains essential for scenarios where the format string is built dynamically at runtime, as f-strings are evaluated immediately at creation time.