String Formatting with F-Strings — Creating Dynamic Output
Imagine you're building a program that needs to display a personalized greeting to each user: "Welcome, Alice!" for one user and "Welcome, Bob!" for another. How do you combine text with variable values to create that message? Or suppose you have a price like 19.5 and want to display it as "$19.50" with exactly 2 decimal places. This lesson teaches you f-strings—Python's modern, clean way to create formatted text by embedding variables and expressions directly into strings.
F-strings let you express your intent clearly: instead of struggling with complicated syntax, you just write what you want to see, with curly braces marking where variables and values go. This mirrors the AI-Native Learning pattern: describe your intent, let the tool handle the complexity, and focus on understanding the result.
What Are F-Strings?
F-strings (formatted string literals) are strings prefixed with the letter f that let you embed variables and expressions directly inside the string. The f prefix tells Python: "This string contains Python code in curly braces—evaluate it and insert the result."
Here's the basic syntax:
name: str = "Alice"
age: int = 25
# F-string with variable embedding
greeting: str = f"Hello, {name}!"
print(greeting) # Output: "Hello, Alice!"
# F-string with multiple variables
bio: str = f"{name} is {age} years old."
print(bio) # Output: "Alice is 25 years old."
Let's look at an example that shows why f-strings are better than older approaches.
Code Example 3.1: F-String Basics — Variable Embedding
# F-strings: Format strings with embedded variables
name: str = "Alice"
age: int = 25
# Embed variables using {variable_name}
greeting: str = f"Hello, {name}!"
bio: str = f"{name} is {age} years old."
print(greeting) # "Hello, Alice!"
print(bio) # "Alice is 25 years old."
# Concatenation (old way—harder to read with many variables):
greeting_old: str = "Hello, " + name + "!"
# F-string (new way—clearer and more readable):
greeting_new: str = f"Hello, {name}!"
# With multiple variables, concatenation becomes unreadable:
# message_old = "Hello, " + name + "! You are " + str(age) + " years old."
# F-strings stay clear:
message_new: str = f"Hello, {name}! You are {age} years old."
# Validate: Result is always a string
print(f"Type of greeting: {type(greeting)}") # <class 'str'>
print(f"Type of bio: {type(bio)}") # <class 'str'>
Validation checkpoint: Run this code. Notice that greeting and bio are both strings (not a mix of text and numbers). The f-string handled the conversion for you. Also notice that when you embed an integer like 25, it automatically converts to text inside the string.
💬 AI Colearning Prompt
"Why did Python designers choose f-strings as the modern standard? What problems do they solve that older methods (% and .format()) didn't? Show me when I should use each method."
This prompt helps you understand the design decision behind f-strings. You'll learn that Python constantly evolves to make common tasks easier, and f-strings represent that evolution in string formatting.
Expressions Inside F-Strings
F-strings can contain not just variables, but expressions—calculations, method calls, or any Python code that produces a value. This makes them incredibly powerful for creating formatted output.
Code Example 3.2: Expressions in F-Strings
# F-strings can contain expressions: calculations, method calls, etc.
x: int = 10
y: int = 3
text: str = "hello"
# Arithmetic expressions
result: str = f"The sum of {x} and {y} is {x + y}" # "The sum of 10 and 3 is 13"
# Method calls
uppercase: str = f"Uppercase: {text.upper()}" # "Uppercase: HELLO"
length: str = f"Length: {len(text)}" # "Length: 5"
# Practical: Build informative output
user_input: str = " alice "
cleaned: str = user_input.strip()
message: str = f"Your name is {cleaned}, which has {len(cleaned)} characters"
# Output: "Your name is alice, which has 5 characters"
# Complex expressions (but keep them readable!)
quantity: int = 5
price_each: float = 9.99
total: float = quantity * price_each
invoice: str = f"Quantity: {quantity}, Price each: ${price_each}, Total: ${total}"
# Output: "Quantity: 5, Price each: $9.99, Total: $49.95"
# Validate: Result is always string
print(f"Type of result: {type(result)}") # <class 'str'>
print(f"Type of uppercase: {type(uppercase)}") # <class 'str'>
Validation checkpoint: The key insight is that everything inside the curly braces is evaluated as Python code, then converted to a string and inserted. Notice how method calls like .upper() and functions like len() work inside f-strings without any special syntax.
Number Formatting
One of the most useful features of f-strings is formatting numbers with specific decimal places. This is essential when displaying prices, measurements, or percentages.
Code Example 3.3: Number Formatting
# Format numbers in f-strings using format specifiers
price: float = 19.5
pi: float = 3.14159
# Basic number formatting: {value:.2f} means 2 decimal places
formatted_price: str = f"Price: ${price:.2f}" # "Price: $19.50"
formatted_pi: str = f"Pi: {pi:.2f}" # "Pi: 3.14"
# More precise formatting
value: float = 3.14159
formatted_1: str = f"1 decimal: {value:.1f}" # "1 decimal: 3.1"
formatted_3: str = f"3 decimals: {value:.3f}" # "3 decimals: 3.142"
formatted_0: str = f"No decimals: {value:.0f}" # "No decimals: 3"
# Practical: Currency formatting
item_price: float = 15.5
tax_rate: float = 0.08
total_price: float = item_price * (1 + tax_rate)
receipt: str = f"Item: ${item_price:.2f}, Tax: ${item_price * tax_rate:.2f}, Total: ${total_price:.2f}"
# Output: "Item: $15.50, Tax: $1.24, Total: $16.74"
# Scientific notation (if needed)
large_number: float = 1234567.89
scientific: str = f"Scientific: {large_number:.2e}" # "Scientific: 1.23e+06"
# Validate: All results are strings
print(f"Type of formatted_price: {type(formatted_price)}") # <class 'str'>
print(f"Type of receipt: {type(receipt)}") # <class 'str'>
Validation checkpoint: The key syntax is {value:.2f} which means "format the number with 2 decimal places." Let's break this down:
{starts the expressionvalueis the variable to format:separates the variable from the format specification.2fmeans "2 decimal places" (thefstands for "floating point" format)}closes the expression
Understanding this pattern lets you control how numbers appear in your output.
🎓 Instructor Commentary
In AI-native development, clarity of INTENT is everything. F-strings let you express "what the output should show" directly in code. When you ask AI to format data, you're really asking "Help me express this intent clearly." That's specification thinking—a skill that becomes essential in future chapters.
This is the foundational mindset: don't think "How do I remember the syntax?" Instead think "What should the user see?" and use tools (including AI) to express that intent clearly.
Why F-Strings? Comparing Formatting Methods
You might encounter older Python code that uses different formatting methods. Let's see why f-strings are now the modern standard.
Code Example 3.4: Why F-Strings? Comparing Methods
# Three older ways to format strings in Python
name: str = "Bob"
age: int = 30
# Method 1: Old string concatenation (not recommended)
output1: str = "Hello, " + name + ". You are " + str(age) + " years old."
# Method 2: Old % formatting (not recommended—confusing symbols)
output2: str = "Hello, %s. You are %d years old." % (name, age)
# Method 3: .format() method (better, but dated)
output3: str = "Hello, {}. You are {} years old.".format(name, age)
# Method 4: F-strings (modern, recommended)
output4: str = f"Hello, {name}. You are {age} years old."
# All produce the same output
print(output1) # "Hello, Bob. You are 30 years old."
print(output4) # "Hello, Bob. You are 30 years old."
# But f-strings are clearer:
# - You can see variable names directly (name, age)
# - You can use expressions directly ({age + 1})
# - No need to convert types (str(age) not needed)
# - Format specifiers are more intuitive (.2f for decimals)
# In AI-native development, clarity of INTENT matters:
# F-string expresses intent: "I want to say hello to NAME and mention AGE"
intent: str = f"Hello, {name}. You are {age} years old."
print(intent)
Validation checkpoint: Notice that all methods produce identical output, but f-strings are:
- Most readable — You see the actual variable names, not mysterious symbols
- Least error-prone — No need to remember the order of variables or convert types
- Most modern — Written with Python 3.6+ standards in mind
This is why this chapter teaches ONLY f-strings—it's the best tool for the job, and learning multiple methods adds unnecessary cognitive load.
Intent-First Design
Let's explore a powerful way of thinking about formatting: start with your intent (what should the output show?) and then use f-strings to express it.
Code Example 3.5: Intent-First Design
# In AI-native development, start by expressing intent clearly
# Then let implementation (formatting) follow
# Example 1: Describing what output should show
user_name: str = "Alice"
user_score: int = 95
user_percentage: float = 95.5
# INTENT: Show a clear, readable result message
result_message: str = f"{user_name} scored {user_score}% ({user_percentage}% precision)"
print(result_message) # "Alice scored 95% (95.5% precision)"
# Example 2: Multiple formatting options—choose the clearest for your intent
price: float = 19.99
quantity: int = 3
total: float = price * quantity
# Simple format
invoice1: str = f"Total: {total}" # "Total: 59.97"
# Clear format with currency
invoice2: str = f"Total: ${total:.2f}" # "Total: $59.97"
# Very clear with breakdown
invoice3: str = f"Item Price: ${price:.2f}, Quantity: {quantity}, Total: ${total:.2f}"
# "Item Price: $19.99, Quantity: 3, Total: $59.97"
# Choose the format that best expresses intent for your audience
# For receipts, invoice3 is clearest
# Example 3: When writing code, ask yourself: "What does the user need to see?"
# Then use f-strings to express exactly that
test_score: int = 88
passing_score: int = 70
if test_score >= passing_score:
status_message: str = f"Congratulations! You scored {test_score}/100 and passed!"
else:
status_message: str = f"You scored {test_score}/100. You need {passing_score - test_score} more points to pass."
# The f-string expresses intent: show clear, relevant information
print(status_message)
Validation checkpoint: This example shows that formatting isn't just "make it look pretty"—it's expressing intent. Different audiences need different information:
- A customer getting a receipt needs the full breakdown
- A test-taker needs feedback on their performance
- A data analyst might prefer different formatting
The f-string lets you customize output to match intent.
🚀 CoLearning Challenge
Ask your AI Co-Teacher:
"Show me 5 examples of poorly formatted output and well-formatted output for the same data. What makes the difference? How would f-strings improve each?"
Expected Outcome: You'll discover that clear formatting helps users understand information better. You'll also practice the skill of "describing intent before implementation"—a core pattern in AI-native development.
Putting It All Together: Formatted Output in Real Programs
Now let's see how f-strings work in practical programs where you're building output for users.
Code Example 3.6: Practical F-String Applications
# Real-world example: Building a receipt for an online store
product_name: str = "Laptop"
base_price: float = 999.50
discount_percent: float = 10
quantity: int = 1
# Calculate discounted price
discount_amount: float = base_price * (discount_percent / 100)
discounted_price: float = base_price - discount_amount
tax_rate: float = 0.08
tax_amount: float = discounted_price * tax_rate
final_total: float = discounted_price + tax_amount
# Build receipt using f-strings with clear formatting
receipt: str = f"""
========== ORDER RECEIPT ==========
Product: {product_name}
Original Price: ${base_price:.2f}
Discount ({discount_percent}%): -${discount_amount:.2f}
Subtotal: ${discounted_price:.2f}
Tax (8%): ${tax_amount:.2f}
TOTAL: ${final_total:.2f}
====================================
"""
print(receipt)
# Another example: Weather display
temperature_celsius: float = 22.5
humidity_percent: int = 65
city: str = "Portland"
# Express intent: "Show the weather clearly"
weather_display: str = f"Weather in {city}: {temperature_celsius:.1f}°C, {humidity_percent}% humidity"
print(weather_display) # "Weather in Portland: 22.5°C, 65% humidity"
# Example: Formatted data table (without using loops)
person1_name: str = "Alice"
person1_age: int = 25
person1_score: float = 92.5
person2_name: str = "Bob"
person2_age: int = 30
person2_score: float = 88.3
# Create formatted output (note: this shows the concept; real tables use loops in Chapter 18)
data_line: str = f"{person1_name:15} | Age: {person1_age:2} | Score: {person1_score:.1f}"
print(data_line) # "Alice | Age: 25 | Score: 92.5"
Validation checkpoint: These examples show real situations where f-strings make your code clearer and your output more user-friendly. Notice how format specifiers like :.2f and :.1f ensure consistent decimal places across your output.
✨ Teaching Tip
If you get a TypeError trying to embed a non-string in an f-string (like a list), ask your AI: "How do I convert types inside f-strings?" You might use
str()to explicitly convert, or extract just the value you need. Understanding when and why conversions are needed teaches you about Python's type system.
Common Mistakes and How to Avoid Them
Mistake 1: Forgetting the f prefix
# ❌ WRONG - This doesn't embed variables, it's just text
greeting: str = "Hello, {name}!" # Literally "Hello, {name}!"
# ✓ RIGHT - The f prefix tells Python this is an f-string
greeting: str = f"Hello, {name}!" # "Hello, Alice!" (if name = "Alice")
Mistake 2: Using the wrong quotes inside f-strings
# ❌ WRONG - Can't use same quotes inside
message: str = f"He said "hello"" # Syntax error
# ✓ RIGHT - Use different quotes or escape characters
message: str = f'He said "hello"' # 'He said "hello"'
Mistake 3: Forgetting the colon for format specifiers
# ❌ WRONG - Format specifier without colon
price: str = f"${price.2f}" # Error
# ✓ RIGHT - Format specifier with colon
price: str = f"${price:.2f}" # "$19.50"
Try With AI
Use your Claude Code or Gemini CLI companion for this structured practice. These prompts progress from basic recall through analysis, helping you master f-strings with AI as your learning partner.
Prompt 1: Recall/Understand — "F-String Basics"
I'm learning about f-strings.
- What's the syntax for putting a variable in an f-string?
- Why are f-strings better than concatenation with +?
- What happens if I forget the f before the quote?
Show me examples.
Expected outcome: You'll learn that f-string syntax uses curly braces for variables, understand the readability advantage, and know that the f prefix is essential.
Prompt 2: Apply — "Format Output Task"
Write Python code that:
- Defines name, age, city variables
- Uses an f-string to create a message: "[Name] is [Age] years old and lives in [City]"
- Uses format specifiers to format a price with 2 decimal places: ${price:.2f}
- Uses an expression inside an f-string to show calculation result
Show me the code and test it.
Expected outcome: You'll apply f-string syntax, variable embedding, format specifiers, and expressions in working code.
Prompt 3: Analyze — "Format Specifier Patterns"
I'm experimenting with number formatting:
- What does {value:.2f} mean?
- What does {value:.0f} mean?
- Why would I use {value:.3f} instead of {value:.2f}?
- What happens if I use {value:.2d} with a float?
Show me examples of each.
Expected outcome: You'll understand format specifier syntax (:.Nf where N is number of decimals), predict output format for different specifiers, and learn error cases (wrong format specifier for data type).
Prompt 4: Synthesize — "Clear Output Design"
I have data about a product:
name = "Laptop"
price = 999.50
discount_percent = 10
How would I use f-strings to create clear output showing:
1. Original price
2. Discount amount (calculated)
3. Final price (calculated)
4. A complete, readable receipt
Show me different options and explain which is clearest.
Expected outcome: You'll design clear output using f-strings; understand that format choices express intent; think about audience needs (what information matters most). This connects f-strings to the broader skill of clear communication through code.