String Formatting in Python

In the world of programming, especially when developing in Python, there is often a need to not only output static strings, but also dynamically embed data into them in order to display information to the user in a convenient and understandable form. This requires the use of special methods that allow you to format strings in such a way that they can include variables, calculation results, and other dynamic elements.

These methods are called – string formatting.

String formatting — is the process of creating string representations of data, which can include both static text and dynamic elements such as variables, expressions, and other data. The purpose of string formatting is to provide a convenient and understandable way to present information, which is especially important when displaying data to a user or writing it to files.

String formatting not only improves the readability and interpretability of data, but is also a key aspect in creating professional and efficient applications. In Python, thanks to its flexibility and powerful built-in functions, developers have several options to achieve this goal.

History of development

String formatting in Python has gone through several stages of development, from the earliest versions of the language to modern implementations that provide great flexibility and ease of use. Here's a quick overview of the key points of this development:

Operator %

Operator % for string formatting has been available since the very beginning of Python's existence, that is, since the first version of the language. Python 1.0, released in January 1994, already supported this operator. It was inspired by a similar operator in the C language and was used to insert values ​​into string templates using placeholders.

Placeholders – these are places in the string where the substitution will be performed. They have a specific syntax.

For formatting we can use the following placeholders:

Syntax

Definition

%s

to insert rows

%d

to insert integers

%f

to insert fractional numbers. For this type, you can also define the number of digits in the fractional part via a dot

%e

outputs a number in scientific notation

%g

to automatically select between normal and exponential notation.

%%

multiplies the value by 100 and adds a percentage sign

%x

To represent integer values ​​in hexadecimal

When calling the % method, values ​​are passed to it as arguments, which are inserted in place of the placeholders.

  1. Formatting strings and integers:

name = "Alice"
age = 30
print("My name is %s and I am %d years old." % (name, age))
  1. Formatting fractional numbers with precision:

pi = 3.14159
print("The value of pi is approximately %.2f." % pi)
# Вывод: The value of pi is approximately 3.14.
  1. Formatting using a comma as a thousand separator:

number = 1234567
print("The number is %d and with commas it looks like %d." % (number, number))
# Вывод: The number is 1234567 and with commas it looks like 1234567.
  1. Formatting numbers in scientific notation:

number = 123456789
print("The number in scientific notation is %e." % number)
# Вывод: The number in scientific notation is 1.234568e+08.
  1. Formatting percentages:

percentage = 0.75
print("The percentage is %.2f%%." % (percentage * 100))
# Вывод: The percentage is 75.00%.

Using Dictionaries to Format Strings with the Operator % allows you to reference variables by name, which makes the formatting process more convenient and easier to modify in the future.

This approach eliminates the need to keep track of the order of the values ​​being passed, since each value is associated with a corresponding key in the dictionary. However, this method requires more code.

name = "Alice"
errno = 0xbadc0ffee
print(
    'Hello %(name)s, an error with code 0x%(errno)x has occurred!' % {
        "name": name, "errno": errno
    }
)

# Вывод: 'Hello Alice, an error with code 0xbadc0ffee has occurred!'

Additional formatting options

  • Field width: You can specify the minimum width of the field in which the value will be displayed. For example, %10s will right align the string in a 10 character wide field.

name = "Alice"
print("|%10s|" % name)
# Вывод: |     Alice|
number = 42
print("|%-10d|" % number)  # Выравнивание по левому краю
print("|%+10d|" % number)  # Отображение знака
# Вывод: |123456789 |
# Вывод: |+123456789|
number = 42
print("|%010d|" % number)  # Заполнение нулями
# Вывод: |0000000042|

Operator % for string formatting, although useful and widely used, had several critical shortcomings:

  1. Operator % had limited formatting capabilities, especially in terms of controlling alignment, padding, and other aspects.

  2. Operator % was vulnerable to string formatting attacks such as malicious code injection via unvalidated input.

  3. And of course, the operator % was not flexible enough.

All these shortcomings led to the fact that in 2008, a new method for formatting was added, which was called – format. It is also interesting that it was available not only in the new version of Python 3.0, but also in the update for the old version of the language (Python 2.6).

Format method

This method inherits all the capabilities of the operator % and at the same time expanded its functionality.

The advantages of this method are:

  • Ability to explicitly use named and positional arguments.

  • Enhanced formatting functionality, such as control over alignment, padding, precision, and thousands separator.

  • Improving safety and eliminating possible injections.

  • Extendable by overriding the method __format__ in custom classes

  • Improved readability by explicitly specifying arguments and allowing named parameters.

Now the general syntax of a placeholder looks like this: {:плейсхолдер}. This means that placeholders are now enclosed in curly braces, and the operator % was replaced by :.

Depending on the placeholder, additional parameters can be added. For example, to format float numbers, the following parameters can be used
{:[количество_символов][запятая][.число_знаков_в_дробной_части] плейсхолдер}

When calling the format method, values ​​are passed to it as arguments, which are inserted in place of the placeholders:

name = "Python"
welcome_phrase = "Hello {:s}!"
print(welcome_phrase.format(name))
# Вывод: Hello Python!

As a result of the method format() returns a new formatted string. This means that we can assign the result of the operation to a variable:

your_welcome = welcome_phrase.format(name)

If the number being formatted is greater than 999, we can specify in the placeholder definition that we want to use a comma as a decimal separator:

source = "{:,d} символов"
print(source.format(5000))   
# Вывод: 5,000 символов

For fractional numbers, that is, those that represent the float type, before the placeholder code after the dot, you can specify how many digits in the fractional part we want to display:

number = 12.3456789
print("{:.2f}".format(number))   # 12.34
print("{:.3f}".format(number))   # 12.345
print("{:.4f}".format(number))   # 12.3456
print("{:,.2f}".format(12345.12345))    # 12,345.12

Another important innovation was that format began to support date and time:

from datetime import datetime
now = datetime.now()
print("Today is {:%Y-%m-%d}.".format(now))
# Вывод: Today is 2024-07-21.

Despite the fact that this method has all the necessary functionality – this was not the limit. So on December 23, 2016, Python 3.6 was released, which included…

f-strings

F-strings provide a convenient and efficient way to embed Python expressions directly into string literals, making them a very popular and preferred method of string formatting in modern Python.

F-lines are denoted by the prefix f or F before a string literal and allow Python expressions to be included inside the curly braces {}which are then calculated and substituted into the string. For example:

name = "Alice"
age = 30
print(f"Hello, {name}! You are {age} years old.")
# Вывод: Hello, Alice! You are 30 years old.

What about placeholders?

By using the curly brace syntax, F-strings support all the standard formatting specifiers we covered in the previous methods.

You can also use special characters to set the length of a string when formatting:

  • aligns a string to the left and pads it with spaces on the right side up to length N

  • >N: aligns a string to the right and pads it with spaces on the left side up to length N

  • ^N: aligns the string to the center and pads it with spaces on the left and right sides up to length N

  • .N: specifies the exact length of the string. If it has more than N characters, it is truncated

For example, if you want to add “0” characters to the left (to right align), to the right (to left align), or to the left and right (to middle align) of the original string until the length reaches 9 characters:

print(f"{123:0>9}") # Вывод: 000000123
print(f"{123:0<9}") # Вывод: 123000000
print(f"{123:0^9}") # Вывод: 000123000

Now we will take a closer look at the capabilities and innovations of this formatting method, which you may not have known about:

Small useful things

1) Since Python 3.8, f-strings support a special syntax for printing variable names along with their values. This is called “debugging”. The syntax is as follows:

x = 10
y = 25
print(f"{x = }, {y = }")

2) F-strings allow Python expressions to be embedded directly into string literals. This makes the code more compact and readable. For example:

a = 5
b = 10
print(f"The sum of {a} and {b} is {a + b}.")
# Вывод: The sum of 5 and 10 is 15.

3) There are advantages in OOP too. So methods __repr__ And __str__ in Python are used to create string representations of objects. By default, the method used to create a readable string representation of class instances is __str__. However, if it is required to use the method __repr__ instead of __str__you can use the conversion flag !r in f-strings.

Here is an example demonstrating the use of these methods:

class User:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    def __str__(self):
        return f"{self.first_name} {self.last_name}"
    def __repr__(self):
        return f"User('{self.first_name}', '{self.last_name}')"


user = User("Pablo", "Chikoni")
print(f"{user}")  # Вывод: Pablo Chikoni
print(f"{user!r}")  # Вывод: User('Pablo', 'Chikoni')

Optimization

Another advantage is that f-strings are highly optimized and work faster than other string formatting methods such as str.format() or operator %. This makes them a preferred choice for many tasks, especially where high performance is required.

I will try to prove this to you with the following example:

import timeit

# Примеры строк для форматирования
name = "Alice"
age = 30

# Функция для измерения времени выполнения
def measure_time(stmt, number=1000000):
    return timeit.timeit(stmt, number=number, globals=globals())

# Измерение времени выполнения для каждого метода
f_string_time = measure_time('f"My name is {name} and I am {age} years old."')
format_time = measure_time('"My name is {} and I am {} years old.".format(name, age)')
percent_time = measure_time('"My name is %s and I am %d years old." % (name, age)')

# Вывод результатов
print(f"f-string time: {f_string_time:.6f} seconds")
print(f"str.format() time: {format_time:.6f} seconds")
print(f"operator % time: {percent_time:.6f} seconds")

will output:

f-string time: 0.166108 seconds
str.format() time: 0.330560 seconds
operator % time: 0.276897 seconds

As you can see, the execution speed differs almost 2 times from all previous formatting methods. So following Zen Pythonnamely the point “There should be one and only one obvious way to do something in Python”. We would recommend you to use f-strings.

If you liked this article, you can subscribe to our telegram channel. You will find many more interesting and useful articles in it!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *