# String Formatting: Domain Exploration **Date:** 2026-01-03 **Source:** python-numbers-everyone-should-know benchmarks **Python Version:** 3.14.2 (CPython, ARM64 macOS) --- ## Executive Summary String formatting performance: **simple concatenation is fastest for trivial joins**, while **f-strings offer the best balance of readability and performance** for interpolation use cases. --- ## Raw Benchmark Results | Operation | Time (ns) | Throughput | |-----------|-----------|------------| | `concat_small` | 39.1 ns | 25.6M ops/sec | | `f_string` | 64.9 ns | 15.4M ops/sec | | `percent_formatting` | 89.8 ns | 11.1M ops/sec | | `format_method` | 103 ns | 9.7M ops/sec | ### Relative Performance | Method | vs f-string | |--------|-------------| | `concat_small` | 1.66x faster | | `f_string` | 1.00x (reference) | | `percent_formatting` | 0.72x slower | | `format_method` | 0.63x slower | --- ## Why F-Strings Are Fast F-strings are parsed at **compile time**, not runtime: 1. **No method lookup**: F-strings don't call `.format()` at runtime 2. **No tuple creation**: `%` formatting requires `(name,)` tuple 3. **Specialized bytecode**: `FORMAT_VALUE` and `BUILD_STRING` are optimized --- ## When to Use Each Method ### Concatenation Wins For 2-3 literal strings with no formatting: ```python path = base_dir + '/' + filename # Simpler, faster ``` ### % Formatting for Logging ```python # Deferred evaluation - string built only if debug enabled logger.debug('Processing %s items', count) # f-string - string ALWAYS built, then discarded logger.debug(f'Processing {count} items') # Wasteful ``` ### .format() for Dynamic Templates ```python template = get_template_from_config() # Returns 'User: {name}' result = template.format(name=user.name) ``` --- ## Practical Rules for Coding Agents ### Rule 1: Default to F-Strings ```python # Preferred message = f'User {user.name} logged in at {timestamp}' ``` ### Rule 2: Use Concatenation for Trivial Joins ```python url = base_url + endpoint # Fine - simpler and faster ``` ### Rule 3: Use join() for Multiple Parts ```python # Correct - O(n) time result = ''.join([part1, part2, part3, part4]) # Inefficient - O(n^2) time result = part1 + part2 + part3 + part4 ``` ### Rule 4: Keep % for Logging ```python logger.info('Processed %d records in %.2fs', count, elapsed) ``` --- ## Summary | Scenario | Best Choice | Reason | |----------|-------------|--------| | Variable interpolation | f-string | 1.6x faster than `.format()` | | Simple 2-part join | Concatenation | 1.7x faster than f-string | | Building from many parts | `''.join()` | O(n) vs O(n^2) | | Logging statements | `%` style | Deferred evaluation | | Dynamic templates | `.format()` | Template flexibility | --- *Benchmark source: python-numbers-everyone-should-know*