В мире программирования, особенно при разработке на 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|
  • Заполнение нулями: Можно заполнить пустое пространство нулями, используя флаг 0.

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: задает точную длину строки. Если в ней больше 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-строки.

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

Комментарии (12)


  1. Wiggin2014
    21.07.2024 08:11
    +3

    Зачем???

    Эволюция форматирования строк в Python https://habr.com/p/828396/


    1. Chikkl Автор
      21.07.2024 08:11

      Доброго времени суток! Я не знал про этот пост, и считаю, что тема в этой статье разобрана более подробно.


    1. vassabi
      21.07.2024 08:11
      +4

      затем, чтобы

        вы можете подписаться на наш телеграмм канал.

      PS: интересно, когда запилят читалку статей, чтобы такие абзацы автоматически уходили под кат "тут реклама" ?


      1. Chikkl Автор
        21.07.2024 08:11

        действительно, а прочитать материал моей статьи и посмотреть ту, что скинули вы не удосужились. Ничего что там и половины нет того, что я здесь рассказал?


        1. vassabi
          21.07.2024 08:11

          мне лично очень нравится ваша статья, всю её прочитал, кроме самого последнего абзаца.


          1. Chikkl Автор
            21.07.2024 08:11

            Меня это сильно радует, спасибо за эти слова.

            Однако я не понимаю, почему рассказать, о месте, где я делюсь контентом для новичков - это плохо.

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


  1. artrone
    21.07.2024 08:11

    Отличная статья, понятным языком изложена


    1. Chikkl Автор
      21.07.2024 08:11

      Благодарю вас!


  1. rivkat
    21.07.2024 08:11

    Еще есть

    from string import Template


    1. Chikkl Автор
      21.07.2024 08:11

      Доброго времени, да, этот модуль присутствует "из коробки". Однако, я не стал относить его к стандартным в том понимании, которое изложил в статье. Всё таки это импортируемый модуль)


  1. invasy
    21.07.2024 08:11
    +4

    Зачем тут тег «Спортивное программирование»?

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

    А остальные почему обделили вниманием?

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

    целых десятичных чисел

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

    Совершенно неверно. Своим примером кода это и подтверждаете.

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

    Это ведь не так. А про field_name и conversion для format() вообще не написано.

    Лучше всё-таки почитать документацию.


    1. Chikkl Автор
      21.07.2024 08:11

      Благодарю вас за замечания, я обязательно исправлю все эти недочёты!