Python f-string
last modified May 11, 2025
This Python f-string tutorial demonstrates how to format strings efficiently using f-strings, the preferred approach for string interpolation in modern Python. With f-strings, developers can create dynamic and readable output in a concise and intuitive way.
Python f-string is a powerful and flexible string formatting method
introduced in Python 3.6. Unlike older formatting techniques such as
%
-based formatting and str.format()
, f-strings are
faster, more readable, and less prone to errors. They simplify string
manipulation while maintaining excellent performance.
An f-string is denoted by the f
prefix and allows embedded
expressions inside curly brackets {}
. These expressions are
evaluated at runtime, making f-strings a great choice for constructing dynamic
strings. For example:
name = "Alice" age = 30 print(f"Hello, my name is {name} and I am {age} years old.")
In this example, Python automatically replaces {name}
and
{age}
with their corresponding values. This feature makes f-strings
more intuitive compared to older approaches, improving code readability.
F-strings also support format specifiers that control numerical precision,
alignment, and padding. Format specifiers are added after a colon
(:
) inside the curly brackets. For instance,
f'{price:.3f}'
ensures that the floating-point number stored in
price
is rounded to three decimal places:
price = 19.98765 formatted_price = f"{price:.3f}" print(formatted_price) # Output: 19.988
Beyond basic formatting, f-strings enable advanced operations such as inline calculations, function calls, and even conditional expressions. This versatility makes them suitable for a wide range of applications in Python development, from constructing log messages to formatting financial data.
Using f-strings enhances code efficiency and readability while reducing the complexity of string manipulations. They are now considered the best practice for formatting strings in Python, replacing older methods in most use cases.
Python string formatting
The following example demonstrates three different ways to format strings in
Python. It shows the evolution from the oldest percent-style formatting, to
the more modern str.format()
method, and finally to the concise
and powerful f-string syntax introduced in Python 3.6.
#!/usr/bin/python name = 'Peter' age = 23 print('%s is %d years old' % (name, age)) print('{} is {} years old'.format(name, age)) print(f'{name} is {age} years old')
The example formats a string using two variables.
print('%s is %d years old' % (name, age))
This is the oldest option. It uses the %
operator
and classic string format specifies such as %s
and %d
.
print('{} is {} years old'.format(name, age))
Since Python 3.0, the format
function was introduced to provide
advance formatting options.
print(f'{name} is {age} years old')
Python f-strings are available since Python 3.6. The string has the f
prefix and uses {}
to evaluate variables.
$ python main.py Peter is 23 years old Peter is 23 years old Peter is 23 years old
Expressions
The example below demonstrates how you can include expressions directly inside an f-string. Any valid Python expression can be placed within the curly braces, allowing you to perform calculations or manipulate data inline as you format your output.
#!/usr/bin/python bags = 3 apples_in_bag = 12 print(f'There are total of {bags * apples_in_bag} apples')
Here, the expression bags * apples_in_bag
is evaluated inside the
f-string, and its result is inserted into the output. This makes f-strings
very flexible for dynamic string construction.
$ python main.py There are total of 36 apples
Using dictionaries
F-strings can also access values from dictionaries. You can reference dictionary keys directly within the curly braces, making it easy to display structured data in your output.
#!/usr/bin/python user = {'name': 'John Doe', 'occupation': 'gardener'} print(f"{user['name']} is a {user['occupation']}")
In this example, the f-string retrieves the values associated with the
'name'
and 'occupation'
keys from the dictionary and
inserts them into the output string.
$ python main.py John Doe is a gardener
The f-string debug option
Python 3.8 introduced a handy feature for debugging: the self-documenting
expression. By adding an equals sign (=
) after an expression in an
f-string, Python will print both the expression and its value, making it easier
to trace calculations and variable states.
#!/usr/bin/python import math x = 0.8 print(f'{math.cos(x) = }') print(f'{math.sin(x) = }')
This example outputs both the expression and its result, which is especially useful for debugging or logging intermediate values in your code.
$ python main.py math.cos(x) = 0.6967067093471654 math.sin(x) = 0.7173560908995228
Multiline f-strings
F-strings can span multiple lines, making it easy to format longer messages or blocks of text. You can use triple quotes to create a multiline f-string and embed variables or expressions on any line.
#!/usr/bin/python name = 'John Doe' occupation = 'gardener' age = 34 msg = f'''name: {name} age: {age} occupation: {occupation}''' print(msg)
This example constructs a multiline string that includes several variables. Multiline f-strings are useful for generating formatted reports or messages.
$ python main.py name: John Doe age: 34 occupation: gardener
Calling functions
You can call functions directly inside f-strings. This allows you to display the result of a function call inline, making your output more dynamic and reducing the need for temporary variables.
#!/usr/bin/python def mymax(x, y): return x if x > y else y a = 3 b = 4 print(f'Max of {a} and {b} is {mymax(a, b)}')
Here, the mymax
function is called inside the f-string, and its
return value is included in the output. This technique is helpful for
presenting computed results directly in your strings.
$ python main.py Max of 3 and 4 is 4
The f-string objects
F-strings can also display objects. If the object defines a __str__
or __repr__
method, Python will use that method to convert the
object to a string for display. This makes it easy to show custom objects in
your output.
#!/usr/bin/python class User: def __init__(self, name, occupation): self.name = name self.occupation = occupation def __repr__(self): return f"{self.name} is a {self.occupation}" u = User('John Doe', 'gardener') print(f'{u}')
In this example, the User
class defines a __repr__
method. When the object is used in an f-string, Python calls this method to
produce the string representation.
$ python main.py John Doe is a gardener
The __format__ method
The __format__
method allows you to customize how your objects are
formatted within f-strings. By defining this method, you can control the output
based on the format specifier provided in the f-string, enabling advanced and
flexible formatting for your custom classes.
#!/usr/bin/python from dataclasses import dataclass @dataclass class User: name: str occupation: str def __format__(self, spec): return f'User(name={self.name}{spec} occupation={self.occupation})' u1 = User('John Doe', 'gardener') u2 = User('Roger Roe', 'driver') u3 = User('Lucia Smith', 'teacher') print(f'{u1:-}') print(f'{u2:;}') print(f'{u3:#}')
In this example, the __format__
method inserts the format specifier
between the object's fields. This approach is useful for custom display logic in
complex applications.
$ python main.py User(name=John Doe- occupation=gardener) User(name=Roger Roe; occupation=driver) User(name=Lucia Smith# occupation=teacher)
Escaping characters
Sometimes you need to include special characters, such as curly braces or quotes, in your f-strings. To do this, you must escape them. Curly braces are escaped by doubling them, and single quotes can be escaped with a backslash.
#!/usr/bin/python print(f'Python uses {{}} to evaluate variables in f-strings') print(f'This was a \'great\' film')
The first line shows how to display curly braces in the output, while the second line demonstrates escaping a single quote. Proper escaping ensures your strings are displayed as intended.
$ python main.py Python uses {} to evaluate variables in f-strings This was a 'great' film
Python f-string format datetime
F-strings can be used to format date and time objects. By providing a format
specifier after a colon, you can control the output of datetime
objects, such as displaying the date, time, weekday, or other components in a
custom format.
#!/usr/bin/python import datetime now = datetime.datetime.now() print(f'{now:%Y-%m-%d %H:%M}') print(f'{now:%A}') print(f'{now:%a}') print(f'{now:%B}') print(f'{now:%j}') print(f'{now:%w}')
This example shows several ways to format the current date and time using
f-strings. The format codes after the colon follow the conventions of the
strftime
method.
$ python main.py 2024-03-13 11:38 Wednesday Wed March 073 3
Nested expressions
You can nest expressions inside f-strings, including using variables as format specifiers. This allows for highly dynamic formatting, such as switching between different date formats or other output styles at runtime.
#!/usr/bin/python from datetime import datetime today = datetime.now().date() spec1 = '%d.%m.%y' spec2 = '%y/%m/%d' print(f'{today:{spec1}}') print(f'{today:{spec2}}')
Here, the format specifier is stored in a variable and then used inside the f-string. This technique is useful for creating flexible and reusable formatting code.
$ python main.py 16.03.24 24/03/16
Formatting floats
F-strings make it easy to control the number of decimal places when displaying floating point numbers. You can specify the precision directly after the colon, ensuring your output is as precise or as concise as you need.
#!/usr/bin/python val = 12.3 print(f'{val:.2f}') print(f'{val:.5f}')
The first line prints the value with two decimal places, while the second line shows five decimal places. This is especially useful for financial or scientific applications where precision matters.
$ python main.py 12.30 12.30000
Thousands separators
You can use f-strings to add thousands separators to large numbers, making them easier to read. Both underscores and commas are supported as grouping characters, which is helpful for displaying financial, statistical, or scientific data.
#!/usr/bin/python val = 1_200_400_001 print(val) print(f'{val:_}') print(f'{val:,}')
The output shows the number in its raw form, with underscores, and with commas. This makes it much easier to interpret large values at a glance.
$ python main.py 1200400001 1_200_400_001 1,200,400,001
Percentage
F-strings can also be used to display percentages. By using the percent format
specifier (%
), you can easily convert a decimal value to a
percentage, and you can control the number of decimal places shown.
#!/usr/bin/python val = 1/7.0 print(f'{val}') print(f'{val:.2%}')
The first line prints the raw decimal value, while the second line displays it as a percentage with two decimal places. This is useful for reports and data analysis.
$ python main.py 0.14285714285714285 14.29%
Format width
You can set the width of formatted values in f-strings, which is useful for aligning columns in tables or reports. You can also specify fill characters, such as zeros, to pad values that are shorter than the desired width.
#!/usr/bin/python for x in range(1, 11): print(f'{x:02} {x*x:3} {x*x*x:4}')
This example prints three columns: the first is zero-padded to two digits, the second is right-aligned to three spaces, and the third is right-aligned to four spaces. This approach is ideal for creating neatly formatted tables.
$ python main.py 01 1 1 02 4 8 03 9 27 04 16 64 05 25 125 06 36 216 07 49 343 08 64 512 09 81 729 10 100 1000
Justifying strings
F-strings allow you to justify strings to the left, right, or center within a
given width. This is controlled by the >
, <
, and
^
characters after the colon. Justification is useful for aligning
text in tables or output.
#!/usr/bin/python words = ['sky', 'fork', 'small', 'cup', 'car', 'war'] for word in words: print(f'|{word:>20}|') for word in words: print(f'|{word:^20}|') for word in words: print(f'|{word:<20}|')
The first loop right-aligns each word, the second centers it, and the third left-aligns it. Each output line is exactly 20 characters wide, making it easy to create aligned columns of text.
$ python main.py | sky| | fork| | small| | cup| | car| | war| | sky | | fork | | small | | cup | | car | | war | |sky | |fork | |small | |cup | |car | |war |
Numeric notations
F-strings can display numbers in a variety of notations, including hexadecimal, octal, binary, and scientific notation. This is useful for debugging, data analysis, or when working with different numeric systems.
#!/usr/bin/python val = 300 # hexadecimal lower print(f'{val:x}') # hexadecimal upper print(f'{val:X}') # octal print(f'{val:o}') # binary print(f'{val:b}') # scientific print(f'{val:e}')
This example prints the same value in several different notations, showing how easy it is to switch between formats using f-strings.
$ python main.py 12c 12C 454 100101100 3.000000e+02
Formatting Bytes and Bytearrays with F-Strings
Python's f-strings support direct formatting of bytes
and
bytearray
objects, allowing for efficient representation of binary
data in string output. This is useful when working with encoded data, network
packets, or file processing.
#!/usr/bin/python data = b'\x48\x65\x6c\x6c\x6f' # Byte sequence representing "Hello" byte_array = bytearray([72, 101, 108, 108, 111]) # Equivalent bytearray print(f"Bytes (hex): {data.hex()}") # Output: 48656c6c6f print(f"Bytearray (ASCII): {byte_array.decode()}") # Output: Hello
F-strings enable direct conversion of bytes
objects into
hexadecimal representation using hex
, while
bytearray
supports decoding into readable text using
decode
. These methods enhance the usability of binary data in
formatted output.
Formatting Enums
Python's Enum
class allows defining symbolic constants, and
f-strings provide an intuitive way to format and display their values. Enums can
be represented by their names or assigned values.
#!/usr/bin/python from enum import Enum class Status(Enum): SUCCESS = 1 FAILURE = 2 PENDING = 3 status = Status.SUCCESS print(f"Status name: {status.name}") # Output: SUCCESS print(f"Status value: {status.value}") # Output: 1
Using f-strings, enum members can be dynamically formatted by accessing their
.name
and .value
attributes, ensuring meaningful
output in applications requiring structured constants.
Unicode and Special Characters
Python f-strings fully support Unicode, which means you can easily include non-ASCII characters, accented letters, and even emojis in your formatted output. This is especially useful for internationalization or when you want to display special symbols in your strings.
#!/usr/bin/python name = "Zoë" emoji = "\U0001F600" # 😀 print(f"User: {name} {emoji}")
In this example, the f-string combines a name with an accented character and an emoji. F-strings handle these characters natively, ensuring correct rendering and output in your terminal or application.
Formatting Numbers: Leading Zeros and Digit Grouping
F-strings provide powerful options for formatting numbers. You can pad numbers with leading zeros to ensure fixed-width output, or use commas as thousands separators to make large numbers easier to read. These features are especially helpful for reports, tables, or any output where alignment and clarity matter.
#!/usr/bin/python number = 42 big_number = 1234567 print(f"{number:05}") # Output: 00042 (5-digit width, zero-padded) print(f"{big_number:,}") # Output: 1,234,567 (comma as a thousands separator)
Padding with zeros ensures numbers line up in columns, while grouping digits
with commas improves readability for large values. The colon (:
)
inside the curly braces lets you specify these formatting options directly in
the f-string.
Using List Comprehensions and Lambdas
F-strings can evaluate any valid Python expression inside the curly braces. This includes list comprehensions and lambda functions, allowing you to generate and display dynamic content on the fly. This feature is useful for quickly showing computed results or summaries without extra code.
#!/usr/bin/python nums = [1, 2, 3] print(f"Squares: {[x**2 for x in nums]}") # List comprehension inside f-string add = lambda a, b: a + b print(f"Sum: {add(5, 7)}") # Lambda function execution inside f-string
By embedding expressions like list comprehensions or lambda calls, you can produce concise, readable output that reflects real-time calculations or data transformations, all within a single f-string.
Dynamic Width and Precision with Variables
F-strings allow you to dynamically set the width and precision of your formatted output by using variables instead of hardcoded values. This provides great flexibility when you need to adjust formatting based on runtime conditions or user preferences.
#!/usr/bin/python value = 123.456789 width = 10 precision = 3 # Dynamic width and precision using variables print(f"Fixed format: {value:{width}.{precision}f}") # Adjust precision based on the value large_value = 12345.6789 precision = 1 if large_value > 10000 else 3 print(f"Adaptive format: {large_value:.{precision}f}")
The first example uses variables for both width and precision, allowing you to control formatting parameters programmatically. The second example demonstrates how to change the precision based on the value's magnitude, which is useful for keeping output consistent when values have different scales.
$ python main.py Fixed format: 123.457 Adaptive format: 12345.7
Custom Fill Characters and Alignments
F-strings support custom fill characters for padding when aligning text. Instead of just spaces or zeros, you can use any character to fill the extra space, which is useful for creating visual separators or decorative output.
#!/usr/bin/python title = "TITLE" # Center with asterisks print(f"{title:*^30}") # Right-align with dashes print(f"{title:->30}") # Left-align with dots print(f"{title:.<30}") # Numbers can use custom fill characters too number = 42 print(f"{number:#>10}")
The syntax is {value:fill_char align width}
, where fill_char is any
single character, align is one of ^
(center), >
(right), or <
(left), and width is the total field width. This
gives you precise control over how your output appears.
$ python main.py ************TITLE************ ------------------------TITLE TITLE........................ ########42
Formatting Numbers with Sign Control
F-strings offer precise control over how positive and negative numbers are displayed, including whether to show the plus sign for positive numbers. This is important for financial reports, scientific notation, and any context where the sign of a number matters.
#!/usr/bin/python positive = 42 negative = -42 zero = 0 # Always show the sign (+ or -) print(f"Always show sign: {positive:+d} {negative:+d} {zero:+d}") # Only show the - sign for negative numbers (default) print(f"Default behavior: {positive:d} {negative:d} {zero:d}") # Show space for positive, - for negative (useful for alignment) print(f"Space for positive: {positive: d} {negative: d} {zero: d}") # Combine with width for aligned columns print(f"Aligned column: {positive:+5d} {negative:+5d} {zero:+5d}")
The +
flag ensures that both positive and negative signs are shown,
which is useful for data that could be either positive or negative. The space
flag (
) reserves space for the sign but only shows it for negative
values, helping maintain column alignment while reducing clutter.
$ python main.py Always show sign: +42 -42 +0 Default behavior: 42 -42 0 Space for positive: 42 -42 0 Aligned column: +42 -42 +0
Formatting Complex Numbers
F-strings can also format complex numbers, allowing you to control the display of their real and imaginary parts. You can specify precision for both parts independently if needed, though standard float formatting options apply.
#!/usr/bin/python c = 3.14159 + 2.71828j # Default formatting print(f"Default: {c}") # Format with precision for both parts print(f"Precision: {c:.2f}") # Accessing real and imaginary parts separately print(f"Real: {c.real:.3f}, Imaginary: {c.imag:.3f}j")
The .2f
specifier in {c:.2f}
applies to both the real
and imaginary parts of the complex number. You can also access and format the
.real
and .imag
attributes individually for more granular
control.
$ python main.py Default: (3.14159+2.71828j) Precision: (3.14+2.72j) Real: 3.142, Imaginary: 2.718j
Nested F-Strings
F-strings can be nested, meaning an f-string can be part of an expression inside another f-string. This allows for complex, dynamic string constructions, though it's important to maintain readability by not overusing deep nesting.
#!/usr/bin/python name = "there" precision = 2 value = 3.14159 # Nested f-string to dynamically set precision print(f"Hello, {name}! Value: {value:.{precision}f}") # Another example with a conditional expression price = 49.99 print(f"Item: {"Book"}, Price: ${f"{price:.2f}" if price > 0 else "Free"}")
In the first example, {precision}
inside {value:.{precision}f}
demonstrates how an inner f-string expression can define a format specifier for
an outer one. The second example shows an f-string used within a conditional
expression inside the main f-string.
$ python main.py Hello, there! Value: 3.14 Item: Book, Price: $49.99
Source
Python f-string - language reference
In this article we have worked with Python f-strings.
Author
List all Python tutorials.