Positional formatting is a powerful and flexible technique in Python for inserting variable values into strings using placeholders that are replaced at runtime. Before f-strings became the preferred method in Python 3.6+, positional formatting with str.format() was the go-to approach — it uses curly brace placeholders {} (positional by default) or explicit indices {0}, {1}, and supports format specifiers for alignment, padding, precision, and type conversion. In 2026, while f-strings are usually recommended for simplicity and speed, .format() remains relevant — especially in legacy code, dynamic format strings, internationalized applications (with str.format_map()), and cases where placeholders need to be generated or reused. It’s still widely used in logging, templating, and older libraries.
Here’s a complete, practical guide to positional formatting with str.format(): basic positional placeholders, explicit indexing, format specifiers, real-world patterns, comparison with f-strings, and modern best practices with type hints, safety, and pandas/Polars usage.
Basic positional formatting uses empty {} placeholders — values are inserted in the order they are passed to .format().
name = "Alice"
age = 25
occupation = "programmer"
output = "My name is {}, I am {} years old, and I work as a {}.".format(name, age, occupation)
print(output)
# My name is Alice, I am 25 years old, and I work as a programmer.
Explicit positional indexing with {0}, {1} allows reuse of values and reordering — useful when the same value appears multiple times or order needs to change.
output = "My name is {0}, I am {1} years old, and I work as a {2}. Hi again, {0}!".format(name, age, occupation)
print(output)
# My name is Alice, I am 25 years old, and I work as a programmer. Hi again, Alice!
# Reorder arguments
reordered = "Occupation: {2}, Name: {0}, Age: {1}".format(name, age, occupation)
print(reordered)
# Occupation: programmer, Name: Alice, Age: 25
Format specifiers control alignment, padding, precision, and type — syntax inside braces: {:[alignment][width][.precision][type]}.
price = 19.99
quantity = 3
total = price * quantity
# Currency with 2 decimal places, right-aligned width 10
print("Price: ${:>10.2f}".format(price)) # Price: $ 19.99
# Percentage with 1 decimal
print("Tax rate: {:.1%}".format(0.0875)) # Tax rate: 8.8%
# Zero-padded integer
print("Item ID: {:05d}".format(42)) # Item ID: 00042
# Mixed: left-aligned name, right-aligned price
print("{:<10} ${:>8.2f}".format("Coffee", 4.50))
# Coffee $ 4.50
Real-world pattern: dynamic logging, reports, or templating — positional formatting is ideal when the format string is stored or generated separately.
# Log template from config
log_template = "User {0} logged in at {1:%Y-%m-%d %H:%M:%S} from IP {2}"
user = "alice"
login_time = datetime.now()
ip = "192.168.1.100"
log_message = log_template.format(user, login_time, ip)
print(log_message)
# User alice logged in at 2026-02-10 14:30:45 from IP 192.168.1.100
Best practices make positional formatting safe, readable, and performant. Prefer f-strings for most cases — f"{var}" is faster, clearer, and less error-prone than .format(). Use .format() when the format string is dynamic (e.g., from config, database, or user input) — template.format(**data_dict) with keyword args. Modern tip: use Polars for large string formatting — pl.col("name").str.format("Hello {}") is fast and vectorized. Add type hints — str — improves static analysis. Use named placeholders for clarity — "{name} is {age}".format(name=name, age=age) or format_map(dict). Handle missing keys — format_map() raises KeyError; use dict.get() or default values. For internationalization, use str.format_map() with gettext — supports positional and named args. Avoid % operator — deprecated in favor of .format() and f-strings. Combine with str.join() — ", ".join(["{:.2f}".format(x) for x in values]) formats lists cleanly.
Positional formatting with .format() inserts values into strings flexibly — positional, named, or indexed placeholders with specifiers for alignment, precision, and type. In 2026, prefer f-strings for static formats, use .format() for dynamic templates, vectorize in pandas/Polars, and add type hints for safety. Master positional formatting, and you’ll create dynamic messages, logs, reports, and templates cleanly and correctly.
Next time you need to insert variables into a string template — reach for .format() or f-strings. It’s Python’s cleanest way to say: “Put these values here, in the right format.”