F-strings Performance and Deferred Evaluation Limitations
Core Performance Reality
F-strings are evaluated at runtime, not compile time. PEP 498 explicitly states: “It should be noted that an f-string is really an expression evaluated at run time, not a constant value.” The expressions “are evaluated in the context where the f-string appeared” with “full access to local and global variables.”
Performance advantages come from:
- More efficient parsing and code generation vs
.format()
- Avoiding method call overhead that
.format()
requires - Direct expression evaluation rather than runtime string parsing
The Eager Evaluation Problem
F-strings are always evaluated, creating a critical performance issue in logging contexts. The logging module uses lazy evaluation - it only interpolates strings that match the current logging level.
# BAD - f-string always evaluated regardless of log level
logging.debug(f"Processing {expensive_calculation()}")
# GOOD - only evaluated if debug level is active
logging.debug("Processing %s", expensive_calculation())
This matters because “if you call debug() a million times inside a loop, then Python will eagerly evaluate its argument, and the interpolation will happen a million times. This behavior will add performance overhead to your code.”
Proposed Solutions
PEP 501 - Deferred Evaluation (Currently Deferred)
Originally proposed “i-strings” with deferred evaluation:
logging.debug(i"Event: {expensive_call()}") # Only evaluated if needed
Status: “Deferred pending further experience with PEP 498’s simpler approach”
Recent Revival Efforts (2023)
Nick Coghlan and collaborators proposed reopening PEP 501 after 7 years, arguing:
- F-strings are now widely adopted and understood
- PEP 701 provides better grammatical foundation
- Real-world use cases have emerged (SQL injection prevention, logging performance)
PEP 750 - Template Strings
Introduces t-strings
as a generalization of f-strings for custom string processing, though still with eager evaluation.
Current State
The community recognizes this as a fundamental limitation. One developer noted: “PEP 498 really botched it. The deferred evaluation described by PEP 501 absolutely should have been baked into the core f-string implementation.”
For now: Use f-strings for general formatting, but stick with %
formatting for logging contexts where lazy evaluation matters.
Sources
- PEP 498 - Literal String Interpolation - Original f-strings specification
- PEP 501 - General purpose string interpolation - Deferred evaluation proposal
- PEP 750 - Template Strings - Template string generalization
- Python Discussions: PEP 501 Reopen - 2023 revival effort
- Real Python: F-String Lazy Evaluation in Logging
Created: 2025-01-28
Tags: python performance logging pep498 pep501