Last modified: Nov 01, 2026

Python String Formatting: format vs f-string vs %

Python offers three main ways to format strings. You can use the percent operator, the str.format() method, or f-strings. Each method has strengths and weaknesses. This guide compares them with clear examples. It also shares best practices for clean code.

String formatting matters in logs, reports, and user messages. The right choice improves readability and performance. Beginners often ask which method to use. The short answer is f-strings for most new code. But older code and special cases still use the other two.

The Percent Operator (%)

The percent operator is the oldest method. It works like C-style printf. You place %s or %d inside a string. Then you pass values with the % operator.

 name = "Alice" age = 30 text = "Name: %s, Age: %d" % (name, age) # Old-style formatting print(text) 
 Name: Alice, Age: 30 

This method is simple for basic cases. It supports flags for padding and precision. For example, %.2f shows two decimal places. But the syntax becomes hard to read with many values. Named placeholders use a dictionary, which adds verbosity.

Percent formatting is still common in logging. The logging module uses %-style formatting for lazy evaluation. That means the string is formatted only if the log level is enabled. This is a key performance benefit. Do not replace logging %-style with f-strings in hot paths.

When you format error messages for output, you may need to send them to stderr. This Python print to stderr guide shows how to do that correctly.

The str.format() Method

The str.format() method arrived in Python 2.6. It uses curly braces as placeholders. You can use positional or named arguments. It also supports advanced formatting rules.

 name = "Bob" score = 95.5 text = "Name: {}, Score: {:.1f}".format(name, score) # Method-based formatting print(text) 
 Name: Bob, Score: 95.5 

Named placeholders improve readability. You can reuse values without repeating arguments. The method also supports indexing and attribute access. This makes it flexible for templates.

 data = {"name": "Eve", "score": 88} text = "Name: {name}, Score: {score}".format(**data) # Named placeholders print(text) 
 Name: Eve, Score: 88 

One advantage of str.format() is deferred formatting. You can store a template string and format it later. This is useful for translations and configuration files. f-strings cannot do that because they evaluate immediately.

If you need to control line breaks after formatted output, see this guide on Python print without newline for practical patterns.

f-strings (Formatted String Literals)

f-strings arrived in Python 3.6. They are the newest and most concise method. Prefix a string with f and put expressions inside braces. Python evaluates the expressions at runtime.

 name = "Mia" score = 91.25 text = f"Name: {name}, Score: {score:.2f}" # f-string formatting print(text) 
 Name: Mia, Score: 91.25 

f-strings are fast and readable. They support the same format specifiers as str.format(). You can embed arithmetic, method calls, and conditional expressions. The debugging shortcut {variable=} is also handy.

For a deeper dive into syntax and advanced features, read this complete guide to Python f-strings. It covers multiline strings, nested braces, and performance tips.

One limitation is immediate evaluation. That makes f-strings unsuitable for templates that need delayed formatting. They also cannot be used as logging format strings with lazy evaluation. Use %-style for logging instead.

Side-by-Side Comparison

Here is the same output using all three methods. Compare the syntax and readability.

 name = "Sam" age = 25 # Percent operator print("Name: %s, Age: %d" % (name, age)) # str.format() print("Name: {}, Age: {}".format(name, age)) # f-string print(f"Name: {name}, Age: {age}") 
 Name: Sam, Age: 25 Name: Sam, Age: 25 Name: Sam, Age: 25 

The f-string is shortest and clearest. The percent version is compact but less flexible. The str.format() version is explicit but verbose. For new code, f-strings usually win.

Performance Considerations

f-strings are generally the fastest for simple interpolation. They avoid method lookup and argument parsing. Percent formatting is also fast and can be competitive in some cases. str.format() is often slower due to its flexibility.

But do not optimize prematurely. Readability matters more in most applications. For logging, use %-style with the logging module. That avoids formatting strings that are never emitted. Lazy formatting is a real performance win in high-volume logs.

For large templates, str.format() may be better. It separates the template from the data. That improves maintainability. Choose based on context, not microbenchmarks alone.

Common Pitfalls and Best Practices

Avoid mixing styles in the same file. Consistency makes code easier to review. Do not use f-strings for user-supplied templates. They evaluate expressions immediately. Use a safe template engine instead.

Be careful with quotes inside f-string expressions. Older Python versions restrict reusing the same quote type. Use different quotes or compute the value first. Also watch for backslashes inside expressions.

Keep expressions short. If an f-string becomes complex, assign intermediate variables. This improves debugging and readability. Use format specifiers for numbers and alignment. Test empty strings and special characters.

Remember that print() adds a newline by default. Use the end parameter if you need different behavior. The linked guide above covers that in detail.

Conclusion

Python string formatting has evolved over time. The percent operator is old but still useful for logging. str.format() offers flexibility and deferred formatting. f-strings provide the cleanest syntax for most new code. Use f-strings for readability, percent for logging, and str.format() for templates. Understand each method’s tradeoffs. Then choose the right tool for the job.