Программирование на Python

Строковые представления в Python - общие сведения о repr () и str ()

Знайте их обычаи и отношения

В предыдущем посте я говорил о f-строках в Python. Вкратце, f-строки известны как форматированные строковые литералы, которые позволяют нам писать более сжатый код для форматирования строк так, как мы хотим. Давайте быстро освежим в памяти струны f. В приведенном ниже коде мы объявляем строковую переменную name и вызываем функцию print() для записи сообщения в консоль.

# The basic use of f-string
>>> name = "Python"
>>> print(f"Hello, {name}!")
Hello, Python!

Как видите, мы устанавливаем f-строку в качестве аргумента для функции print(). По сути, мы используем букву f (заглавная F тоже работает) перед обычной строкой для обозначения создания f-строки. Внутри строки мы заключаем переменную в фигурные скобки, чтобы переменная интерполировалась. В более широком смысле переменная не обязательно должна быть типа str, она может быть int, float, list , dict, set и любого типа.

Более того, мы можем сделать больше с интерполяцией переменной, предоставив дополнительные параметры форматирования. Один из возможных вариантов форматирования - показать преобразование переменной с помощью ! R или ! S, который отобразит строковое представление объекта путем вызова функции repr() или str(). , соответственно.

>>> print(f"Hello, {name!r}!")
Hello, 'Python'!
>>> print(f"Hello, {name!s}!")
Hello, Python!

Как показано в приведенном выше фрагменте кода, параметр ! S дает нам то же сообщение, что и раньше - сообщение без параметра преобразования, а параметр ! R дает нам сообщение с дополнительной парой одинарных кавычек вокруг строки.

Это почему? Давайте узнаем об этом в этой статье.

Обзор repr () и str ()

Прежде чем мы обсудим их соответствующие обычаи, давайте сначала разберемся, что они собой представляют. Как вы, возможно, знаете, обе функции являются встроенными функциями Python, которые доступны для использования сразу после загрузки любого интерпретатора Python. Чтобы вызвать функции repr() и str(), вы можете поместить объект или переменную Python в круглые скобки, потому что по умолчанию каждый объект Python совместим с обеими функциями либо напрямую (например, встроенные типы данных), либо косвенно (например, пользовательские классы с помощью создание подкласса универсального класса object).

>>> grades = {'John': 90}
>>> scores = [100, 99, 95]
>>> record = (404, 'code')
>>> name = 'John'
>>> number = 95
>>> 
>>> for item in (grades, scores, record, name, number):
...     print(f"{str(type(item)):<15}| repr: {repr(item):<15}| str: {str(item)}")
... 
<class 'dict'> | repr: {'John': 90}   | str: {'John': 90}
<class 'list'> | repr: [100, 99, 95]  | str: [100, 99, 95]
<class 'tuple'>| repr: (404, 'code')  | str: (404, 'code')
<class 'str'>  | repr: 'John'         | str: John
<class 'int'>  | repr: 95             | str: 95

Приведенный выше фрагмент кода показывает, что эти встроенные типы поддерживают функции repr() и str(). В большинстве случаев вызов этих двух функций дает одинаковые результаты, за исключением типа данных str, разницу в котором мы видели выше.

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

>>> # Declare a custom class
>>> class Car:
...     def __init__(self, make):
...         self.make = make
... 
>>> car = Car("BMW")
>>> print(f"{repr(car)} | {str(car)}")
<__main__.Car object at 0x107fe2550> | <__main__.Car object at 0x107fe2550>

В приведенном выше коде я объявляю настраиваемый класс Car, создаю экземпляр car и вызываю для экземпляра функции repr() и str(). Как видите, оба просто показывают одно и то же, сообщая нам, что это объект-экземпляр класса Car и его адрес в памяти. Хотя они поддерживают идею о том, что все в Python, похоже, совместимо с этими двумя функциями, но в показанной информации нет ничего слишком захватывающего, верно?

>>> # Implement the __repr__ and __str__ methods
>>> class Car:
...     def __init__(self, make):
...         self.make = make
...
...     def __repr__(self):
...         return "Car repr(): __repr__"
...
...     def __str__(self):
...         return "Car str(): __str__"
... 

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



Посмотрим, что произойдет с обновленным классом, когда мы вызовем функции repr() и str() с помощью экземпляра.

>>> car = Car("BMW")
>>> print(f"{repr(car)} | {str(car)}")
Car repr(): __repr__ | Car str(): __str__

Как видите, под капотом функции repr() и str() реализуются специальными методами __repr__ и __str__ соответственно. Кстати, многие из них построены -in функции реализуются с использованием этих специальных методов (например, len() vs. __len__, abs() vs. __abs__).

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

Рекомендации по использованию

Ключевые отличия

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

Для настраиваемых классов практическое правило реализации этих двух методов заключается в том, что вы определяете метод __repr__ для целей разработки и отладки, а метод __str__ - для представления информации об объекте осмысленным образом.

  • В частности, когда мы пишем код Python в интерактивной консоли, метод repr() неявно вызывается всякий раз, когда мы пишем переменную в приглашении. Консоль покажет нам, что реализовано для метода __repr__ конкретной переменной.
    Например, следующее произойдет, если мы просто предоставим консоли экземпляр car. Как и ожидалось, вызывается метод __repr__.
>>> car
Car Repr: __repr__
  • Однако, когда мы пытаемся распечатать экземпляр car, угадайте, что произойдет?
    Как показано ниже, вызывается метод __str__, что означает, что когда мы вызываем функцию print(), Python неявно использует метод __str__ для получения строкового представления объекта. В большинстве случаев мы получаем информацию об объекте, вызывая функцию print(), и поэтому метод __str__ в идеале должен показывать, какие данные есть у объекта.
>>> print(car)
Car Str: __str__

Следовательно, с точки зрения ключевого различия между repr() и str(), вы можете рассматривать следующее как общее руководство.

__repr__ предназначен для разработчиков. Основная цель - объяснить что это за объект недвусмысленно.

__str__ предназначен для конечных пользователей. Основная цель - объяснить какие данные содержит объект в удобочитаемой форме.

Внедрение передового опыта

С помощью этого общего правила мы можем увидеть, как мы должны реализовать эти две функции для пользовательского класса - класса Car, как было объявлено ранее. Я просто буду откровенен и покажу вам, как правильно реализовать. Рассмотрим обновленную версию ниже.

>>> # Update __repr__ and __str__
>>> class Car:
...     def __init__(self, make):
...         self.make = make
...
...     def __repr__(self):
...         return f"Car({self.make!r})"
...
...     def __str__(self):
...         return f"Car Instance Object, make: {self.make}"
...

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

>>> car = Car('Toyota')
>>> car
Car('Toyota')

Как показано выше, все работает так, как мы и ожидали. Для разработчиков мы точно знаем, что это за объект - в данном случае это объект-экземпляр класса Car. Что еще более важно, это представление - это та же самая строка, которую мы использовали ранее для создания экземпляра. Заметили это?

Что касается реализации метода __repr__, следует подчеркнуть, что мы указываем параметр ! R, чтобы отображать make экземпляра, а не параметр ! S. Использование опции! S очень важно для того, чтобы строковое представление было однозначным. Рассмотрим неправильное использование ниже.

>>> print(f"Car({car.make})")
Car(Toyota)
>>> print(f"Car({car.make!s})")
Car(Toyota)

Как показано выше, при использовании вместо этого параметра ! S строковое представление очень сбивает с толку, потому что оно не дает точной информации о правильном способе создания экземпляра (т. Е. Отсутствие кавычек для make ).

Напротив, для метода __str__ мы хотим отображать информацию о данных объекта для пользователей наших продуктов. Как показано в приведенном ниже коде, когда мы используем функцию print(), мы получим более описательные информация об объекте. Что еще более важно, строка более читабельна для конечных пользователей.

>>> print(car)
Car Instance Object, make: Toyota

Нерешенный вопрос

Понимание различий использования ! R в __repr__ методе пользовательского класса Car поможет нам решить вопрос, с которым мы столкнулись ранее. Помните следующее?

>>> print(f"Hello, {name!r}!")
Hello, 'Python'!
>>> print(f"Hello, {name!s}!")
Hello, Python!

Причина этого различия в том, что для объекта str name метод repr() создает строку в кавычках, а метод str() просто создает обычную строку , как показано в приведенном ниже коде. Таким образом, когда они интерполируются в f-строку, одинарные кавычки будут сохранены при указании параметра ! R.

>>> repr(name)
"'Python'"
>>> str(name)
'Python'

repr () или str ()

В наших пользовательских классах обычно рекомендуется реализовать оба метода. Однако, если вы хотите реализовать только один из них, используйте метод __repr__, потому что, когда Python не может найти собственный метод __str__, он будет использовать метод __repr__ как запасной вариант.

Однако обратное неверно. Другими словами, если Python не может найти реализацию метода __repr__, он просто покажет очень основную информацию об объекте (например, класс и адрес памяти). Конечно, если ни один из них не реализован, Python просто покажет основную информацию для обеих функций.

Выводы и ссылки

Спасибо, что прочитали эту статью. Надеюсь, вы лучше понимаете нюансы между функциями repr() и str(). Напомним, функция repr() предназначена для отображения однозначной информации об объекте с точки зрения разработчика с точки зрения того, как объект может быть построен, а функция str() - для отображения описательной информации об объекте. объект с точки зрения конечного пользователя с точки зрения того, какие данные содержит объект.

Вот несколько ссылок, имеющих отношение к текущему обсуждению.