Python Topics : String Formatting
Interpolating and Formatting Strings in Python
string interpolation involves generating strings by inserting other strings or objects into specific places in a base string or template
string interpolation using an f-string
>>> name = "Bob"

>>> f"Hello, {name}!"
'Hello, Bob!'
with string interpolation may need to format the interpolated values to produce a well-formatted final string
three different tools for string interpolation
  1. f-strings
  2. str.format()
  3. modulo operator (%)
Using F-Strings to Format Strings
Using the Formatting Mini-Language With F-Strings
when using f-strings to create strings through interpolation, need to use replacement fields
with f-strings can define a replacement field using curly brackets ({})
>>> debit = 300.00
>>> credit = 450.00

>>> f"Debit: ${debit}, Credit: ${credit}, Balance: ${credit - debit}"
'Debit: $300, Credit: $450.0, Balance: $150.0'
to format the values and always display two digits on its decimal part can use a format specifier
>>> f"Debit: ${debit:.2f}, Credit: ${credit:.2f}, Balance: ${credit - debit:.2f}"
'Debit: $300.00, Credit: $450.00, Balance: $150.00'
the string formatting mini-language is a powerful tool with features including
  • strings alignment
  • conversion between input objects' types
  • numeric values formatting
  • dynamic formatting
Formatting Strings with F-Strings: A Practical Example
example dictionary
the dictionary has the name key to store the student's name
it has the subjects key to hold a list of dictionaries containing the student's performance in each subject.
student = {
    "name": "John Doe",
    "subjects": [
        {
            "name": "Mathematics",
            "grade": 88,
            "comment": "Excellent improvement.",
        },
        {
            "name": "Science",
            "grade": 92,
            "comment": "Outstanding performance.",
        },
        {
            "name": "History",
            "grade": 78,
            "comment": "Needs to participate more.",
        },
        {
            "name": "Art",
            "grade": 85,
            "comment": "Very creative."
        },
    ],
}
the mule
def build_student_report(student):
    report_header = f"Progress Report. Student: {student['name']}"

    total = sum(subject["grade"] for subject in student["subjects"])
    average = total / len(student["subjects"])
    average_report = f"Average: {average:.2f} / 100\n"

    subject_report = "Course Details:\n"
    for subject in student["subjects"]:
        subject_report += (
            # the <15 format specifier to align the name to the left within 15 characters.
            f"{subject['name']:<15} "
            f" Grade: {subject['grade']:3d} "
            f" Comment: {subject['comment']}\n"
        )
        return f"""
        {report_header}
        {average_report}
        {subject_report}
        Thank you for reviewing the progress report.
        """
Using str.format() to Format Strings

can also use the .format() method to format values during string interpolation
in most cases use the .format() method for lazy interpolation
define a template string in some part of the code and then interpolate values in another part
>>> number_template = "The number is {}"
>>> sum_template = "{0} plus {1} is {2}"

>>> number_template.format(42)
'The number is 42'

>>> a = 5
>>> b = 10
>>> sum_template.format(a, b, a + b)
'5 plus 10 is 15'
Using the Formatting Mini-Language With .format()
the str.format() method also supports the string formatting mini-language
feature allows formatting the interpolated values when creating strings through interpolation

Formatting Strings With .format(): Practical Examples
create a sales report for your company
create a report template and fill it with the appropriate data when it is required
the report template
REPORT_TEMPLATE = """
Monthly Sales Report
--------------------
Report Date Range: {start_date} to {end_date}

Number of Transactions: {sep:.>20} {transactions:,}
Average Transaction Value: {sep:.>11} ${avg_transaction:,.2f}

Total Sales: {sep:.>23} ${total_sales:,.2f}
"""
format specifiers used above

specifierdescription
.>20 displays the interpolated value aligned to the right within a space of 20 characters
the dot after the colon works as the fill character
the other format specifiers, .>11 and .>23, have a similar effect.
,displays the preceding number using a comma as the thousands separator
,.2f shows a value as a floating-point number using two decimal places and a comma as the thousands separator

a function to generate the report

# ...

def build_sales_report(sales_data, report_template=REPORT_TEMPLATE):
    total_sales = sum(sale["amount"] for sale in sales_data)
    transactions = len(sales_data)
    avg_transaction = total_sales / transactions

    return report_template.format(
        sep=".",
        start_date=sales_data[0]["date"],
        end_date=sales_data[-1]["date"],
        total_sales=total_sales,
        transactions=transactions,
        avg_transaction=avg_transaction,
    )
function takes sales data and report template as arguments
usage
>>> from sales_report import build_sales_report

>>> sales_data = [
...     {"date": "2024-04-01", "amount": 100},
...     {"date": "2024-04-02", "amount": 200},
...     {"date": "2024-04-03", "amount": 300},
...     {"date": "2024-04-04", "amount": 400},
...     {"date": "2024-04-05", "amount": 500},
... ]

>>> print(build_sales_report(sales_data))

Monthly Sales Report
--------------------
Report Date Range: 2024-04-01 to 2024-04-05

Number of Transactions: .................... 5
Average Transaction Value: ........... $300.00

Total Sales: ....................... $1,500.00
Formatting Strings With the Modulo Operator (%)
Using Conversion Specifiers
summary of the accepted characters and their corresponding order in the specifier
  1. the % character - marks the start of the specifier
  2. an optional mapping key in parentheses - allows the use of named replacement fields like (name)
  3. an optional conversion flag - affects how some conversion types display
  4. an optional minimum field width - allows defining the number of characters to display
  5. an optional precision - consists of a dot character (.) followed by the desired precision
  6. an optional length modifier - is an l or h for long and short integers
  7. a conversion type - specifies how the output string will be formatted, mimicking different data types
several conversion types are available for the modulo operator
allows control the output's format in some way
examples
  • can convert numbers to hexadecimal notation
  • can add whitespace padding to generate nicely formatted tables and reports
conversion types

Conversion TypeDescription
dsigned integer decimal
isigned integer decimal
osigned octal value
xsigned hexadecimal with lowercase prefix
Xsigned hexadecimal with uppercase prefix
efloating-point exponential format with lowercase e
Efloating-point exponential format with uppercase E
ffloating-point decimal format
Ffloating-point decimal format
gfloating-point format
Gfloating-point format
csingle character (accepts integer or single character string)
rstring as per calling repr()
sstring as per calling str()
astring as per calling ascii()
% a percentage character (%) in the result if no argument is converted

examples of using specifiers

>>> "%d" % 123.123  # As an integer
'123'

>>> "%o" % 42  # As an octal
'52'

>>> "%x" % 42  # As a hex
'2a'

>>> "%e" % 1234567890  # In scientific notation
'1.234568e+09'

>>> "%f" % 42  # As a floating-point number
'42.000000'
other formatting options
>>> # Named replacement fields
>>> jane =  {"first_name": "Jane", "last_name": "Doe"}
>>> "Full name: %(first_name)s %(last_name)s" % jane
'Full name: Jane Doe'

>>> # Minimal width of 15 chars
>>> "%-15f" % 3.1416  # Aligned to the left
'3.141600       '
>>> "%15f" % 3.1416  # Aligned to the right
'       3.141600'

>>> # Dynamic width
>>> width = 15
>>> "%-*f" % (width, 3.1416)
'3.141600       '
>>> "%*f" % (width, 3.1416)
'       3.141600'

>>> # Precision
>>> "%.2f" % 3.141592653589793
'3.14'
>>> "%.4f" % 3.141592653589793
'3.1416'
>>> "%.8f" % 3.141592653589793
'3.14159265'
in the first example use named replacement fields in parentheses and a dictionary to provide the values to be interpolated
the second example provides a minimum width for the string in characters
the third example is a dynamic variation of the second
the * symbol allows dynamic insertion the desired width
finally use the precision option to display a floating-point number using different precisions

the modulo operator doesn't support the string formatting mini-language

Using Conversion Flags
the modulo operator also supports conversion flags

FlagDescription
'#'displays numeric values using alternate forms
'0'adds zero padding for numeric values
'-' left-justifies the value
overrides the '0' conversion if both are given
' ' (space)adds a space before a positive number
'+'adds a sign character ('+' or '-') before the value

examples demonstrate the effect of the # flag
prepends the appropriate prefix to the input number depending on the base used
flag is mostly used with integer values

>>> "%o" % 10
'12'
>>> "%#o" % 10
'0o12'
>>> "%x" % 31
'1f'
>>> "%#x" % 31
'0x1f'
below in the first example add zero padding to the input value
next is an example of how to use the minus sign to align a value to the left in a width of ten characters
the space flag allows adding a space before positive numbers
the space disappears when the value is negative
finally use the plus sign so the string always displays whether the input value is positive or negative
>>> "%05d" % 42
'00042'

>>> "%10d" % 42
'        42'
>>> "%-10d" % 42
'42        '

>>> "% d" % 42
' 42'
>>> "% d" % -42
'-42'

>>> "%+d" % 42
'+42'
>>> "%+d" % -42
'-42'
Deciding Which String Formatting Tool to Use
if the desire is for readable syntax, good performance, and an eager interpolation, then f-strings are the answer
if the need is for a tool for doing lazy string interpolation then the .format() method is the way to go
the modulo operator (%) is an old-fashioned tool
not commonly used in modern Python
may find it in legacy Python code

Featuref-strings.format()%
readabilityhighmediumlow
supports lazy evaluationNoYesYes
supports dictionary unpackingNoYesYes
supports the format mini-languageYesYesNo
supports conversion typesYesYesYes
supports conversion flagsYesYesYes
index