Форматирование строк в Python

d56b2400a92434840bc529b9d5b052df.jpg

В мире программирования, особенно при разработке на Python, часто возникает необходимость не просто выводить статические строки, но и динамически встраивать в них данные, чтобы отобразить информацию пользователю в удобном и понятном виде. Это требует использования специальных методов, которые позволяют форматировать строки таким образом, чтобы они могли включать переменные, результаты вычислений и другие динамические элементы.

Эти методы как раз и называются — форматированием строк.

Форматирование строк — это процесс создания строковых представлений данных, которые могут включать в себя как статический текст, так и динамические элементы, такие как переменные, выражения и другие данные. Цель форматирования строк заключается в обеспечении удобного и понятного способа представления информации, что особенно важно при выводе данных пользователю или при записи их в файлы.

Форматирование строк не только улучшает читаемость и интерпретируемость данных, но и является ключевым аспектом при создании профессиональных и эффективных приложений. В Python, благодаря его гибкости и мощным встроенным функциям, разработчики имеют несколько вариантов для достижения этой цели.

История развития

Форматирование строк в Python прошло через несколько этапов развития, начиная с самых ранних версий языка и до современных реализаций, которые обеспечивают высокую гибкость и удобство использования. Вот краткий обзор ключевых моментов этого развития:

Оператор %

Оператор % для форматирования строк был доступен с самого начала существования Python, то есть с первой версии языка. Python 1.0, выпущенный в январе 1994 года, уже поддерживал этот оператор. Он был вдохновлен аналогичным оператором в языке C и использовался для вставки значений в строковые шаблоны с помощью плейсхолдеров.

Плейсхолдеры — это места в строке, куда будет выполнена подстановка. Обладающие определенным синтаксисом.

Для форматирования мы можем использовать следующие плейсхолдеры:

Синтаксис

Определение

%s

для вставки строк

%d

для вставки целых чисел

%f

для вставки дробных чисел. Для этого типа также можно определить через точку количество знаков в дробной части

%e

выводит число в экспоненциальной записи

%g

для автоматического выбора между обычной и экспоненциальной записью.

%%

умножает значение на 100 и добавляет знак процента

%x

Для представления целочисленных значений в шестнадцатеричном виде

При вызове метода % в него в качестве аргументов передаются значения, которые вставляются на место плейсхолдеров.

  1. Форматирование строк и целых чисел:

name = "Alice"
age = 30
print("My name is %s and I am %d years old." % (name, age))
  1. Форматирование дробных чисел с указанием точности:

pi = 3.14159
print("The value of pi is approximately %.2f." % pi)
# Вывод: The value of pi is approximately 3.14.
  1. Форматирование с использованием запятой в качестве разделителя разрядов:

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. Форматирование чисел в экспоненциальной записи:

number = 123456789
print("The number in scientific notation is %e." % number)
# Вывод: The number in scientific notation is 1.234568e+08.
  1. Форматирование процентов:

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

Использование словарей для форматирования строк с помощью оператора % позволяет ссылаться на переменные по именам, что делает процесс форматирования более удобным и упрощает его модификацию в будущем.

Этот подход избавляет от необходимости следить за порядком передаваемых значений, так как каждое значение связывается с соответствующим ключом в словаре. Однако, этот метод требует большего объема кода.

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!'

Дополнительные параметры форматирования

  • Ширина поля:  Можно указать минимальную ширину поля, в котором будет отображаться значение. Например,  %10s выровняет строку по правому краю в поле шириной 10 символов.

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

Оператор % для форматирования строк, хотя и был полезен и широко использовался, но имел несколько критичных недостатков:

  1. Оператор % имел ограниченные возможности для форматирования, особенно в части управления выравниванием, заполнением и другими аспектами.

  2. Оператор %  был уязвим для атак, связанных с форматированием строк, таких как инъекция вредоносного кода через непроверенные входные данные.

  3. Ну и конечно же, оператор % был недостаточно гибким.

Все эти недостатки привели к тому, что в 2008 году, был добавлен новый метод для форматирования, который так и назывался — format. Интересен и тот факт, что он был доступен не только в новой версии Python 3.0, но и в обновлении для старой версии языка (Python 2.6).

Метод format

Данный метод унаследовал все возможности оператора % и при этом расширил его функционал.

Плюсами данного метода стали:

  • Возможность явного использования именованных и позиционных аргументов.

  • Расширение функциональных возможностей форматирования, например управление выравниванием, заполнением, точностью чисел и разделителем разрядов.

  • Улучшение безопасности и избавление от возможных инъекций.

  • Возможность расширения за счёт переопределения метода __format__ в пользовательских классах

  • Улучшение читаемости благодаря явному указанию аргументов и возможности использования именованных параметров.

Теперь общий синтаксис плейсхолдера выглядит так:  {:плейсхолдер}. Это означает, что плейсхолдеры теперь заключаются в фигурные скобки, а оператор % был заменен на :.

В зависимости от плейсхолдера можно добавлять дополнительные параметры. Например, для форматирования чисел float можно использовать следующие параметры
{:[количество_символов][запятая][.число_знаков_в_дробной_части] плейсхолдер}

При вызове метода format в него в качестве аргументов передаются значения, которые вставляются на место плейсхолдеров:

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

В качестве результата метод format() возвращает новую отформатированную строку. Это означает, что мы можем присвоить результат выполнения операции переменной:

your_welcome = welcome_phrase.format(name)

Если форматируемое число больше 999, то мы можем указать в определении плейсхолдера, что мы хотим использовать запятую в качестве разделителя разрядов:

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

Для дробных чисел, то есть таких, которые представляют тип float, перед кодом плейсхолдера после точки можно указать, сколько знаков в дробной части мы хотим вывести:

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

Важным нововведением стало и то, что format стал поддерживать дату и время:

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

Несмотря на то, что этот способ обладает всем необходимым функционалом — это не стало пределом. Так 23 декабря 2016 года была выпущена версия Python 3.6 в которой появились…

f-строки

F-строки предоставляют удобный и эффективный способ встраивания выражений Python прямо в строковые литералы, что делает их очень популярным и предпочтительным методом форматирования строк в современном Python.

F-строки обозначаются префиксом f или F перед строковым литералом и позволяют включать выражения Python внутри фигурных скобок {}, которые затем вычисляются и подставляются в строку. Например:

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

А что с плэйсхолдерами?

Благодаря использование синтаксиса фигурных скобок F-строки поддерживают все стандартные спецификаторы форматирования, которые мы рассматривали в предыдущих методах.

Также с помощью специальных символов можно задать длину строки при форматировании:

  • выравнивает строку по левому краю и дополняет ее пробелами с правой стороны до длины N

  • >N: выравнивает строку по правому краю и дополняет ее пробелами с левой стороны до длины N

  • ^N: выравнивает строку по центру и дополняет ее пробелами с левой и правой стороны до длины N

  • .N: задает точную длину строки. Если в ней больше N символов, то она усекается

Например, если требуется добавить символы »0» слева (для выравнивания по правому краю), справа (для выравнивания по левому краю) или слева и справа (для выравнивания посередине) от исходной строки до достижения длины в 9 символов:

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

А теперь мы более подробно рассмотрим возможности и нововведения данного метода форматирования, про которые вы, возможно, не знали:

Небольшие полезности

1) Начиная с Python 3.8, f-строки поддерживают специальный синтаксис для вывода имен переменных вместе с их значениями. Это называется «отладкой» (debugging). Синтаксис выглядит следующим образом:

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

2) F-строки позволяют встраивать выражения Python прямо в строковые литералы. Это делает код более компактным и читаемым. Например:

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

3) Плюсы есть и в ООП. Так методы __repr__ и __str__ в Python используются для создания строковых представлений объектов. По умолчанию для формирования читаемого строкового представления экземпляров классов используется метод __str__. Однако, если требуется использовать метод __repr__ вместо __str__, можно воспользоваться флагом преобразования !r в f-строках.

Вот пример, демонстрирующий использование этих методов:

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')

Оптимизированность


Следующим плюсом является и то, что f-строки сильно оптимизированы и работают быстрее, чем другие методы форматирования строк, такие как str.format() или оператор %. Это делает их предпочтительным выбором для многих задач, особенно когда требуется высокая производительность.

Постараюсь вам доказать это, следующим примером:

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")

выведет:

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

Как вы можете заметить, скорость выполнения отличается почти в 2 раза, от всех предыдущих методов форматирования. Так что следуя  Дзен Python, а именно пункту «Должен быть один и только один очевидный способ сделать что-то в Python». Мы бы рекомендовали вам использовать f-строки.

Если вам понравилась данная статья, то вы можете подписаться на наш телеграмм канал. В нём вы найдёте ещё множество интересных и полезных статьей!

© Habrahabr.ru