Python f-string debug syntax: using = for variable inspection
python f string debug syntax: Learn the Python f-string debug syntax that prints variable names and values with a single '=' sign, including expressions, format specif...
python f string debug syntax requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Python's f-string debug syntax, introduced in Python 3.8, lets you print a variable name and its value with a single = sign inside the curly braces. Instead of writing f'name={name}', you write f'{name=}'. The output includes the variable name, an equals sign, and the value. This shorthand is particularly useful for quick debugging because it eliminates the need to repeat the variable name manually.
What the = Does in F-Strings
The = sign inside an f-string tells Python to print the expression text and its evaluated value. For a simple variable, the expression text is the variable name. For example:
name = "Ada" print(f"{name=}") # Output: name='Ada'
The output is exactly what you would get from f"name={name!r}" because the debug syntax uses repr() by default. This is intentional: it makes the output unambiguous for debugging, showing quotes around strings and the repr representation of other objects.
For numbers, the behavior is straightforward:
count = 42 print(f"{count=}") # Output: count=42
You can also use it with multiple variables in a single f-string. Each variable gets its own =:
x, y = 3, 4 print(f"{x=} {y=}") # Output: x=3 y=4
This is far more readable than the traditional f"x={x} y={y}" and reduces the chance of typos when you are rapidly adding print statements during debugging.
Using the Debug Syntax with Expressions
The = works with any Python expression, not just simple variable names. When you write f"{expression=}", Python prints the source text of the expression followed by its value. This is especially helpful for inline arithmetic or function calls:
a, b = 5, 7 print(f"{a + b=}") # Output: a + b=12
items = [1, 2, 3] print(f"{len(items)=}") # Output: len(items)=3
This feature is not limited to variables. You can inspect attribute access, indexing, and even complex expressions:
class Point: def __init__(self, x, y): self.x = x self.y = y p = Point(2, 3) print(f"{p.x=} {p.y=}") # Output: p.x=2 p.y=3
data = {"key": "value"} print(f"{data['key']=}") # Output: data['key']='value'
The expression text is taken verbatim from the source code, so spaces and operators are preserved. This makes the debug output self-documenting: you see exactly what was evaluated.
Combining with Format Specifiers and Conversion Flags
The debug syntax can be combined with the same format specifiers and conversion flags available in regular f-strings. To add a format specifier, place it after the = and a colon. For example, to print a floating-point number with two decimal places:
ratio = 0.6666667 print(f"{ratio=:.2f}") # Output: ratio=0.67
You can also use conversion flags like !r to force repr() or !s for str(). The debug syntax already defaults to repr(), but you can override it:
name = "Ada" print(f"{name=!s}") # Output: name=Ada
Here, !s forces str() instead of repr(), so the quotes are omitted. This is useful when you want a cleaner output for strings.
For nested expressions, the format specifier applies to the final value. For example:
value = 123.456 print(f"{value * 2=:,.2f}") # Output: value * 2=246.92
The expression value * 2 is evaluated first, then formatted with commas and two decimal places.
Common Pitfalls and Limitations
While the debug syntax is convenient, it has a few limitations that can trip you up if you are not careful.
First, the expression text is exactly what you type, including any spaces. If you write f"{x+1=}", the output will be x+1=..., not x + 1=.... This is usually fine, but it can make output slightly less readable if you prefer spaced operators.
Second, the debug syntax always uses repr() by default. For custom objects, repr() may not be defined meaningfully. If you want a different representation, you must add an explicit conversion flag.
Third, the = syntax does not work with f-strings in Python versions before 3.8. If you are working in a codebase that supports Python 3.7 or earlier, you cannot use it. You will need to fall back to the explicit f"name={name}" form.
Another subtle issue arises when you use the debug syntax with a generator expression or a lambda. The expression text includes the entire generator expression, which can be long and confusing. For example:
values = [1, 2, 3] print(f"{sum(x for x in values)=}") # Output: sum(x for x in values)=6
This works, but the output line becomes verbose. In such cases, it is often clearer to assign the result to a variable first and then use the debug syntax on that variable.
Performance and Production Considerations
Using the debug syntax in production code requires the same care as any f-string: the expression is always evaluated, regardless of whether the output is actually used. If you embed an expensive function call inside a debug f-string that is part of a logging statement, that call runs even when the log level is below the threshold. For example:
import logging logging.debug(f"{expensive_function()=}")
Here, expensive_function() is called every time the statement is executed, even if the debug log level is not enabled. This can lead to unexpected performance overhead in production. The same issue exists with regular f-strings, but the debug syntax makes it easy to add such calls without thinking.
To avoid this, use lazy logging with %s formatting or check the log level explicitly before building the f-string. For example:
if logger.isEnabledFor(logging.DEBUG): logger.debug(f"{expensive_function()=}")
This pattern ensures the expression is only evaluated when the debug level is active.
Another consideration is that debug f-strings are meant for development, not for user-facing messages. The output includes the variable name, which is usually not appropriate for end users. Keep them out of production UI strings and reserve them for logs or console output during development.
Compatibility Across Python Versions
The debug syntax is a feature of Python 3.8 and later. If your project must support Python 3.7 or earlier, you cannot use f"{var=}". The Python documentation explicitly notes this as a new feature in version 3.8.
If you are using a linter or formatter like Black, it will recognize the syntax and format it correctly as long as your target Python version is set to 3.8 or higher. Tools like ruff also support it. However, if you are using a codebase that still runs on Python 3.7, you will get a SyntaxError at runtime. The error message is clear: SyntaxError: f-string: expecting '}' because the = is not recognized.
For projects that need to maintain backward compatibility, you can define a small helper function that mimics the behavior:
def debug(var, name=None): if name is None: name = var.__name__ if hasattr(var, '__name__') else 'var' return f"{name}={var!r}"
But this is less convenient and loses the automatic expression-text feature. In practice, if you are on Python 3.8+, using the built-in syntax is the cleanest approach.
When you upgrade a codebase to Python 3.8, you can gradually replace the most verbose debug prints with the = syntax. The output is slightly different from the old f"x={x}" because it includes quotes around strings, but that is usually an improvement for debugging.
Finally, note that the debug syntax works with f-strings only. It does not apply to str.format() or % formatting. If you are using those older methods, you still need to repeat the variable name manually.