F-strings, introduced with Python 3.6, represent a revolution in the way strings are formatted. They offer a clear, concise, and performant syntax that quickly won over the Python community. If you want to master this essential feature and many other Python concepts, our complete Python course will guide you through your learning journey.
Definition of f-strings
An f-string (formatted string literal) is a type of string in Python that allows you to embed Python expressions directly inside the string. The prefix f or F placed before the quotes tells the Python interpreter that it's an f-string.
The basic syntax is simple: you place your expressions between curly braces {} directly in the string. Python evaluates these expressions at runtime and inserts their result into the final string.
name = "Alice"
age = 28
message = f"Hello, my name is {name} and I'm {age} years old."
print(message)
# Result: Hello, my name is Alice and I'm 28 years old.Before the appearance of f-strings, Python developers mainly used two methods to format strings:
- The
%operator (printf style) - The
.format()method
F-strings surpass both approaches in terms of readability and performance.
Basic syntax and usage
Inserting variables
The most common use of f-strings is to insert variables into a string. You can use any variable defined in the current scope.
first_name = "Marie"
city = "Paris"
profession = "developer"
presentation = f"{first_name} lives in {city} and works as a {profession}."
print(presentation)
# Result: Marie lives in Paris and works as a developer.
Evaluating expressions
F-strings are not limited to simple variables. You can include any valid Python expression, including mathematical calculations, function calls, or methods.
# Mathematical calculations
width = 5
height = 3
print(f"The area of the rectangle is {width * height} m².")
# Result: The area of the rectangle is 15 m².
# Method calls
name = "python"
print(f"Language: {name.upper()}")
# Result: Language: PYTHON
# Function calls
import math
radius = 4
print(f"Circumference: {2 * math.pi * radius:.2f}")
# Result: Circumference: 25.13
Using with data structures
F-strings work perfectly with Python data structures like list, dict, and tuple.
# With a list
fruits = ["apple", "banana", "orange"]
print(f"My favorite fruit is {fruits[0]}.")
# Result: My favorite fruit is apple.
# With a dictionary
person = {"name": "Dupont", "age": 35}
print(f"{person['name']} is {person['age']} years old.")
# Result: Dupont is 35 years old.
# With a tuple
coordinates = (48.8566, 2.3522)
print(f"Paris is located at coordinates {coordinates[0]}, {coordinates[1]}.")
# Result: Paris is located at coordinates 48.8566, 2.3522.
Advanced f-string formatting
Format specifiers
F-strings support format specifiers after the : character in the expression. These specifiers allow you to precisely control the appearance of your data.
# Formatting decimal numbers
pi = 3.141592653589793
print(f"Pi rounded to 2 decimals: {pi:.2f}")
# Result: Pi rounded to 2 decimals: 3.14
print(f"Pi rounded to 4 decimals: {pi:.4f}")
# Result: Pi rounded to 4 decimals: 3.1416
# Scientific notation
large_number = 1234567890
print(f"Scientific notation: {large_number:.2e}")
# Result: Scientific notation: 1.23e+09
Alignment and padding
You can align text to the left, right, or center, and specify a fill character.
text = "Python"
# Left alignment (default for strings)
print(f"|{text:<15}|")
# Result: |Python |
# Right alignment
print(f"|{text:>15}|")
# Result: | Python|
# Center alignment
print(f"|{text:^15}|")
# Result: | Python |
# With a custom fill character
print(f"|{text:-^15}|")
# Result: |----Python-----|
Number formatting
F-strings offer many options to format numbers in a readable way.
# Thousand separator
amount = 1234567
print(f"Amount: {amount:,} €")
# Result: Amount: 1,234,567 €
# With underscore as separator (Python 3.6+)
print(f"Amount: {amount:_} €")
# Result: Amount: 1_234_567 €
# Percentage
ratio = 0.756
print(f"Success rate: {ratio:.1%}")
# Result: Success rate: 75.6%
# Numbers with sign
positive_value = 42
negative_value = -17
print(f"Positive: {positive_value:+d}, Negative: {negative_value:+d}")
# Result: Positive: +42, Negative: -17
Date formatting
F-strings integrate perfectly with the datetime module to format dates.
from datetime import datetime
now = datetime.now()
print(f"Date: {now:%d/%m/%Y}")
# Result: Date: 15/01/2025 (example)
print(f"Time: {now:%H:%M:%S}")
# Result: Time: 14:30:45 (example)
print(f"Full date: {now:%A %d %B %Y}")
# Result: Full date: Wednesday 15 January 2025 (example)
Advanced features
The = flag for debugging (Python 3.8+)
Since Python 3.8, you can use the = sign after an expression to display both the expression and its value. This feature is particularly useful for debugging.
x = 10
y = 20
print(f"{x=}")
# Result: x=10
print(f"{x=}, {y=}")
# Result: x=10, y=20
print(f"{x + y = }")
# Result: x + y = 30
# Combined with formatting
pi = 3.14159
print(f"{pi=:.2f}")
# Result: pi=3.14
Multi-line f-strings
You can create multi-line f-strings using triple quotes.
name = "Jean"
age = 30
city = "Lyon"
card = f"""
=============================
PERSONAL CARD
=============================
Name : {name}
Age : {age} years
City : {city}
=============================
"""
print(card)
Escaping curly braces
If you need to display literal curly braces in an f-string, you must double them.
variable = "value"
# To display literal curly braces
print(f"Here are curly braces: {{}}")
# Result: Here are curly braces: {}
print(f"JSON code: {{\"key\": \"{variable}\"}}")
# Result: JSON code: {"key": "value"}
Using with objects and classes
F-strings work with custom objects using the __str__ and __repr__ methods. You can use the !s and !r specifiers to force the use of one or the other method.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} ({self.age} years old)"
def __repr__(self):
return f"Person(name='{self.name}', age={self.age})"
p = Person("Alice", 25)
# Using __str__ (default)
print(f"Person: {p}")
# Result: Person: Alice (25 years old)
# Force __str__
print(f"Person: {p!s}")
# Result: Person: Alice (25 years old)
# Force __repr__
print(f"Debug: {p!r}")
# Result: Debug: Person(name='Alice', age=25)
Comparison with other formatting methods
Here is a comparative table of the different string formatting methods in Python:
| Method | Syntax | Advantages | Disadvantages |
|---|---|---|---|
| f-string | f"Hello {name}" | Readable, fast, flexible | Python 3.6+ only |
| .format() | "Hello {}".format(name) | Compatible Python 2.7+ | More verbose |
| % operator | "Hello %s" % name | Classic style | Less flexible, obsolete |
| Template | Template("Hello $name") | Safe for user input | Fewer features |
In terms of performance, f-strings are generally faster than other methods because evaluation is done at compile time rather than at runtime.
import timeit
name = "Python"
version = 3.12
# Performance test
time_fstring = timeit.timeit('f"{name} {version}"', globals=globals(), number=1000000)
time_format = timeit.timeit('"{} {}".format(name, version)', globals=globals(), number=1000000)
time_percent = timeit.timeit('"%s %s" % (name, version)', globals=globals(), number=1000000)
print(f"f-string: {time_fstring:.3f}s")
print(f".format(): {time_format:.3f}s")
print(f"% operator: {time_percent:.3f}s")
Best practices
F-strings are recommended by PEP 498 as the preferred formatting method for Python 3.6 and later versions. They improve code readability and reduce errors.
1. Prioritize readability
Avoid overly complex expressions in your f-strings. If the expression becomes difficult to read, assign it to a variable first.
# Avoid
print(f"Result: {[x**2 for x in range(10) if x % 2 == 0]}")
# Prefer
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(f"Result: {even_squares}")
2. Use f-strings for debugging
The = flag is your ally for quickly understanding the state of your variables. Also think about using the print function strategically to display your debug f-strings.
# Quick debugging
def calculate_price(quantity, unit_price, discount):
print(f"{quantity=}, {unit_price=}, {discount=}") # Debug
total = quantity * unit_price * (1 - discount)
print(f"{total=:.2f}") # Debug
return total
3. Document your functions with docstrings
Combine f-strings with docstring to create clear documentation and explicit error messages.
def divide(a, b):
"""Divides a by b and returns the result."""
if b == 0:
raise ValueError(f"Division impossible: {a} / {b} (division by zero)")
return a / b
4. Be careful about security
Never use f-strings with unvalidated user input, as they can execute arbitrary code.
Never construct f-strings from strings coming from untrusted users. For these cases, use the string.Template class instead, which is more secure.
from string import Template
# For user input, prefer Template
user_input = "name"
template = Template("Hello $variable")
result = template.safe_substitute(variable=user_input)
5. Use f-strings for building SQL queries (with caution)
Never build SQL queries directly with f-strings. Always use the parameterization mechanisms of your database library.
# DANGER - NEVER DO THIS
user_name = "Alice"
query = f"SELECT * FROM users WHERE name = '{user_name}'" # Vulnerable!
# CORRECT - Use parameters
cursor.execute("SELECT * FROM users WHERE name = ?", (user_name,))
Common use cases
Report generation
def generate_report(sales):
total = sum(sales)
average = total / len(sales) if sales else 0
report = f"""
╔══════════════════════════════════╗
║ SALES REPORT ║
╠══════════════════════════════════╣
║ Number of sales : {len(sales):>13} ║
║ Total : {total:>13,.2f} € ║
║ Average : {average:>13,.2f} € ║
╚══════════════════════════════════╝
"""
return report
sales = [150.50, 200.00, 89.99, 450.00, 75.25]
print(generate_report(sales))
Logging and error messages
import logging
logging.basicConfig(level=logging.INFO)
def process_order(order_id, client, amount):
logging.info(f"Processing order #{order_id} for {client} - Amount: {amount:.2f}€")
try:
# Processing...
logging.info(f"Order #{order_id} processed successfully")
except Exception as e:
logging.error(f"Error order #{order_id}: {e}")
Building URLs and paths
from pathlib import Path
base_url = "https://api.example.com"
version = "v2"
resource = "users"
user_id = 12345
url = f"{base_url}/{version}/{resource}/{user_id}"
print(url)
# Result: https://api.example.com/v2/users/12345
# File paths
base_folder = Path("/home/user")
file_name = "report_2025"
extension = "pdf"
path = f"{base_folder}/{file_name}.{extension}"
print(path)
# Result: /home/user/report_2025.pdf
Frequently Asked Questions
What is the difference between f-string and the .format() method?
F-strings are more readable and faster than the .format() method. With f-strings, you write variables directly in the string (f"Hello {name}"), while with .format(), you must pass them as arguments ("Hello {}".format(name)). F-strings are also evaluated at compile time, making them more performant. However, .format() remains useful for reusable templates or for compatibility with Python 2.7.
Can you use f-strings with lambda functions?
Yes, you can call lambda functions in f-strings, but you must surround them with parentheses to avoid syntax errors with the : character.
# Correct
square = lambda x: x ** 2
print(f"The square of 5 is {square(5)}")
# Result: The square of 5 is 25
# With an inline lambda (parentheses required)
print(f"Result: {(lambda x: x * 2)(10)}")
# Result: Result: 20
How to display curly braces in an f-string?
To display literal curly braces { and } in an f-string, you must double them. Use {{ to display an opening brace and }} for a closing brace. For example: f"Here are braces: {{value}}" will display Here are braces: {value} without interpreting value as an expression.
How to learn to master f-strings and Python in general?
To master f-strings and the entire Python language, we recommend following our complete Python course on Believemy. This course will guide you step by step through all the essential concepts of the language, from basics to advanced features like f-strings, class, decorators, and much more. Regular practice with concrete exercises is the key to becoming an effective Python developer.