Skip to content

Python's f-string Debugging Trick That Saves Me Hours of Typing

The Problem

I was debugging a function with a dozen variables, and my code looked like this:

debug_nightmare.py
def process_order(order_id, customer_name, items, total, discount, tax):
print(f"order_id={order_id}")
print(f"customer_name={customer_name}")
print(f"items={items}")
print(f"total={total}")
print(f"discount={discount}")
print(f"tax={tax}")
# ... rest of function

I was typing the same variable name twice for every single print statement. It felt wasteful, but I needed to see the variable names alongside their values in the output.

The Repetitive Pattern I Hated

Every debug session looked like this:

tedious_debug.py
x = 10
y = 20
name = "Alice"
scores = [85, 92, 78]
print(f"x={x}") # x=10
print(f"y={y}") # y=20
print(f"name={name}") # name=Alice
print(f"scores={scores}") # scores=[85, 92, 78]

I typed x twice. Then y twice. Then name twice. Every. Single. Time.

Then I discovered Python 3.8 has a feature that does this automatically.

The Solution: f-string Equals Syntax

I found out about the = specifier in f-strings, introduced in Python 3.8:

clean_debug.py
x = 10
y = 20
name = "Alice"
scores = [85, 92, 78]
print(f"{x=}") # x=10
print(f"{y=}") # y=20
print(f"{name=}") # name='Alice'
print(f"{scores=}") # scores=[85, 92, 78]

The output shows both the variable name AND its value. I only type the variable name once.

How It Works

When I use f"{variable=}", Python prints the variable name, an equals sign, and the repr of the value.

equals_demo.py
value = 42
text = "hello"
print(f"{value=}") # value=42
print(f"{text=}") # text='hello'

Notice that strings get quotes in the output. That’s because it uses repr() internally, not str().

It Works With Expressions Too

I was surprised that this works with expressions, not just variables:

expressions.py
x = 10
y = 20
print(f"{x + y=}") # x + y=30
print(f"{len('hello')=}") # len('hello')=5
print(f"{x * 2=}") # x * 2=20

Python prints the exact expression I wrote, then the result. This is useful for quick calculations during debugging.

Spacing Is Preserved

I noticed that the spacing in my expression is preserved in the output:

spacing.py
name = "Alice"
print(f"{name=}") # name='Alice'
print(f"{name =}") # name ='Alice'
print(f"{name = }") # name = 'Alice'

The output matches what I typed. If I add spaces around the =, they appear in the output too.

Before and After Comparison

Here’s how my debugging code changed:

Before (Python 3.7 and earlier):

before_python37.py
def calculate_discount(price, quantity, discount_rate, tax_rate):
print(f"price={price}")
print(f"quantity={quantity}")
print(f"discount_rate={discount_rate}")
print(f"tax_rate={tax_rate}")
subtotal = price * quantity
print(f"subtotal={subtotal}")
discount = subtotal * discount_rate
print(f"discount={discount}")
total = subtotal - discount
print(f"total={total}")
return total

After (Python 3.8+):

after_python38.py
def calculate_discount(price, quantity, discount_rate, tax_rate):
print(f"{price=}")
print(f"{quantity=}")
print(f"{discount_rate=}")
print(f"{tax_rate=}")
subtotal = price * quantity
print(f"{subtotal=}")
discount = subtotal * discount_rate
print(f"{discount=}")
total = subtotal - discount
print(f"{total=}")
return total

Same output, less typing. The output for both:

price=100
quantity=5
discount_rate=0.1
tax_rate=0.08
subtotal=500
discount=50.0
total=450.0

A Real Debugging Session

I was tracking down a bug in a data processing function. Here’s what my debug output looked like:

data_processing.py
def process_user_data(users, filters, settings):
print(f"{len(users)=}")
print(f"{filters=}")
print(f"{settings=}")
active_users = [u for u in users if u.get('active')]
print(f"{len(active_users)=}")
filtered = [u for u in active_users if u.get('age', 0) > 18]
print(f"{len(filtered)=}")
results = apply_settings(filtered, settings)
print(f"{len(results)=}")
return results

The output:

len(users)=150
filters={'min_age': 18, 'status': 'active'}
settings={'limit': 100, 'sort': 'desc'}
len(active_users)=120
len(filtered)=85
len(results)=85

I could quickly see where my data was getting filtered. The len(users)= syntax let me show the expression AND its result in one line.

Formatting Still Works

I can combine the = specifier with format specifiers:

formatting.py
price = 123.456789
ratio = 0.12345
print(f"{price=:.2f}") # price=123.46
print(f"{ratio=:.2%}") # ratio=12.35%

This is useful when I need to see rounded values during debugging.

Gotchas I Learned

Gotcha 1: It Uses repr(), Not str()

repr_vs_str.py
name = "Alice"
print(f"{name}") # Alice (str)
print(f"{name=}") # name='Alice' (repr)

The quotes appear because repr() shows the string representation. This is actually helpful because I can see the type of the value.

Gotcha 2: Complex Expressions Can Be Hard to Read

complex_expressions.py
data = {'users': [{'name': 'Alice', 'age': 30}]}
print(f"{data['users'][0]['name']=}") # data['users'][0]['name']='Alice'

The expression gets printed verbatim, which can look messy. For complex access patterns, I prefer:

cleaner_complex.py
data = {'users': [{'name': 'Alice', 'age': 30}]}
first_user_name = data['users'][0]['name']
print(f"{first_user_name=}") # first_user_name='Alice'

Gotcha 3: Only Works in Python 3.8+

version_check.py
# This will raise SyntaxError in Python 3.7 and earlier
x = 10
print(f"{x=}") # SyntaxError in Python < 3.8

If you’re stuck on an older Python version, you’ll have to use the old way:

old_way.py
x = 10
print(f"x={x}") # Works in all Python 3 versions

When I Actually Use This

I use the = specifier in these scenarios:

1. Quick debugging print statements:

quick_debug.py
def fetch_data(url, timeout, retries):
print(f"{url=}, {timeout=}, {retries=}") # One line, all values
# ... fetch logic

2. Checking intermediate calculations:

intermediate.py
def calculate_total(items):
subtotal = sum(item['price'] for item in items)
tax = subtotal * 0.08
total = subtotal + tax
print(f"{subtotal=}, {tax=}, {total=}") # See all at once
return total

3. Logging variable state in try/except blocks:

error_logging.py
try:
result = risky_operation(data)
except Exception as e:
print(f"Error: {e=}")
print(f"{data=}")
raise

When I Don’t Use It

I don’t use this for production logging or user-facing messages. It’s purely for debugging during development.

production.py
# DON'T use for user messages
print(f"Your balance is: {balance=}") # Confusing for users
# DO use for debug output
# print(f"{balance=}") # Good for debugging

Summary

Python 3.8’s f-string equals syntax (f"{variable=}") saves me from typing variable names twice when debugging. It prints both the expression and its value automatically.

Key points:

  • Use f"{variable=}" instead of f"variable={variable}"
  • Works with expressions: f"{x + y=}"
  • Preserves spacing in the output
  • Uses repr() for values (strings show quotes)
  • Requires Python 3.8 or later

This is one of those small quality-of-life improvements that I use daily. It’s not going to change how I write production code, but it makes debugging sessions much less tedious.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments