Материал подготовлен командой Simple-Server для администраторов VPS и выделенных серверов. Команды и пути проверяйте на тестовой машине перед production.
Разница между str() и repr()
Методы связаны между собой, однако имеют существенные различия по части вывода дополнительной информации:
- Метод
__str__()возвращает фактическое значение переменной в формате строки. Он менее информативен. А еще вызывается функциямиprint(),str(),format().
Метод __str__() возвращает фактическое значение переменной в формате строки. Он менее информативен. А еще вызывается функциями print(), str(), format().
- Метод
__repr__()возвращает репрезентируемое значение переменной в формате кода. Он более информативен. А еще вызывается функциейrepr().
Метод __repr__() возвращает репрезентируемое значение переменной в формате кода. Он более информативен. А еще вызывается функцией repr().
При этом, в отличие от __str__(), результат метода __repr__() можно использовать для воссоздания переменной с помощью функции eval(), которая выполняет код (выражения языка Python) в формате строки.
Грубо говоря, возвращаемое значение метода __str__() ориентировано на пользователя, в то время как результат функции __repr__() больше предназначен для разработчика.**
**
Пример: встроенные типы данные
Для более глубокого понимания отличия между обоими методами лучше всего привести простой, но наглядный пример:
variableInt = 13 # переменная с целочисленным значением
variableString = "13" # переменная со строковым значением
# вывод в консоль типов переменных
print(type(variableInt))
print(type(variableString))
# вывод в консоль целочисленной переменной
print(variableInt.__str__())
print(variableInt.__repr__())
# вывод в консоль строковой переменной
print(variableString.__str__())
print(variableString.__repr__())Консольный вывод этого примера будет следующим:
<class 'int'>
<class 'str'>
13
13
13
'13'Во-первых, переменные однозначно имеют разные типы — целочисленный и строковый.
Во-вторых, для целочисленной переменной методы __str__() и __repr__() возвращают одно и тоже значение. Однако ситуация полностью противоположная для строковой переменной возвращаемой значение метода __repr__() обрамлено одинарными кавычками.
Чтобы понять причину такого поведения, можно немного расширить показанный выше пример:
variableInt = 13
variableString = "13"
print(type(variableInt))
print(type(variableString))
print(variableInt.__str__())
print(variableInt.__repr__())
print(variableString.__str__())
print(variableString.__repr__())
# создание новых переменных (целочисленной и строковой) с помощью функции eval() на основе дополнительной информации, репрезентирующей их
variableIntNew = eval(variableInt.__repr__())
variableStringNew = eval(variableString.__repr__())
# вывод в консольный терминал типов новых переменных
print(type(variableIntNew))
print(type(variableStringNew))
# вывод в консольный терминал значения новых переменных
print(variableIntNew)
print(variableStringNew)На этот раз вывод в консольном терминале будет уже таким:
<class 'int'>
<class 'str'>
13
13
13
'13'
<class 'int'>
<class 'str'>
13
13Как можно заметить, на основе возвращаемого значения метода __repr__() создаются полноценные копии уже существующих переменных — целочисленная и строковая соответственно.
Однако если бы вместо __repr__() использовалась __str__(), то такой результат не был бы достигнут — вместо новой строковой появилась бы новая целочисленная переменная:
variableInt = 13
variableString = "13"
print(type(variableInt))
print(type(variableString))
variableIntNew = eval(variableInt.__str__())
variableStringNew = eval(variableString.__str__())
print(type(variableIntNew))
print(type(variableStringNew))Вывод в консольный терминал после запуска этого примера будет следующим:
<class 'int'>
<class 'str'>
<class 'int'>
<class 'int'>Как видно, обе новые переменные имеют один и тот же тип — целочисленный. Очевидно, это не то поведение, которое хотелось достигнуть.
Таким образом, можно сформулировать разницу между обоими методами довольно простым языком:
- Функция
__str__()возвращает текущее значение переменной.
Функция __str__() возвращает текущее значение переменной.
- Функция
__repr__()возвращает код, способный создать аналогичную переменную с таким же значением.**
**
Функция __repr__() возвращает код, способный создать аналогичную переменную с таким же значением.**
**
import datetime # импорт стандартного модуля для работы с датой и временем
dateNow = datetime.datetime.now() # создание переменной типа даты и времени, которая будет содержать текущее время
print("Тип:", type(dateNow)) # вывод в консольный терминал типа переменной
print("Значение:", dateNow.__str__()) # вывод в консольный терминал значения переменной
print("Репрезентация:", dateNow.__repr__()) # вывод в консольный терминал репрезентации переменнойКонсольный вывод этого примера будет содержать дополнительную информацию о переменной:
Тип: <class 'datetime.datetime'>
Значение: 2024-12-20 05:31:58.749833
Репрезентация: datetime.datetime(2024, 12, 20, 5, 31, 58, 749833)На примере библиотечного класса (в отличие от встроенных типов) хорошо видно, насколько сильно значение переменной (__str__()) может отличатся от ее репрезентации (__repr__()), которая может быть выполнена в качестве выражения Python.
Благодаря такому поведению показанный код можно усложнить, добавив операцию с уже знакомой функцией eval(), создающую копию оригинальной переменной в данном конкретном случае:
import datetime
dateNow = datetime.datetime.now()
print("Тип:", type(dateNow))
print("Значение:", dateNow.__str__())
print("Репрезентация:", dateNow.__repr__())
print("") # вывод в консольный терминал пустой строки для визуального разделения двух других выводов
dateNowSecond = eval(dateNow.__repr__()) # создаем копию переменной времени на основе репрезентации оригинальной переменной
print("Тип:", type(dateNowSecond))
print("Значение:", dateNowSecond.__str__())
print("Репрезентация:", dateNowSecond.__repr__())Вывод в консольный терминал при запуске этого примера будет следующим:
Тип: <class 'datetime.datetime'>
Значение: 2024-12-20 11:52:47.243953
Репрезентация: datetime.datetime(2024, 12, 20, 11, 52, 47, 243953)
Тип: <class 'datetime.datetime'>
Значение: 2024-12-20 11:52:47.243953
Репрезентация: datetime.datetime(2024, 12, 20, 11, 52, 47, 243953)Таким образом, на основе строковой репрезентации оригинальной переменной класса (а не базового типа данных), полученной с помощью метода __repr__(), была создана ее точная копия через функцию eval().
Аналогично примерам с базовыми типами данных, такого результата нельзя достигнуть с помощью метода __str__():
import datetime
dateNow = datetime.datetime.now()
print("Тип:", type(dateNow))
print("Значение:", dateNow.__str__())
print("Репрезентация:", dateNow.__repr__())
print("")
dateNowSecond = eval(dateNow.__str__()) # создаем копию переменной времени на основе значения оригинальной переменной
print("Тип:", type(dateNowSecond))
print("Значение:", dateNowSecond.__str__())
print("Репрезентация:", dateNowSecond.__repr__())После запуска этого примера в консольном терминале возникнет ошибка:
Тип: <class 'datetime.datetime'>
Значение: 2024-12-20 21:32:20.088363
Репрезентация: datetime.datetime(2024, 12, 20, 21, 32, 20, 88363)
Traceback (most recent call last):
File "/home/jdoodle.py", line 11, in <module>
dateNowSecond = eval(dateNow.__str__()) # создаем копию переменной времени на основе значения оригинальной переменной
^^^^^^^^^^^^^^^^^^^^^^^
File "<string>", line 1
2024-12-20 21:32:20.088363
^^
SyntaxError: invalid syntaxЭто происходит потому, что эта запись:
dateNowSecond = eval(dateNow.__str__())Неявно преобразуется в эту:
dateNowSecond = 2024-12-20 21:32:20.088363Разумеется, такое выражение некорректно в языке Python.
Как минимум в показанном примере выполняемый внутри функции eval() код можно поместить в одинарные кавычки:
import datetime
dateNow = datetime.datetime.now()
print("Тип:", type(dateNow))
print("Значение:", dateNow.__str__())
print("Репрезентация:", dateNow.__repr__())
print("")
dateNowSecond = eval("'" + dateNow.__str__() + "'") # выражение обернуто в одинарные кавычки
print("Тип:", type(dateNowSecond))
print("Значение:", dateNowSecond.__str__())
print("Репрезентация:", dateNowSecond.__repr__())Это позволит избежать ошибки, но исказит требуемое поведение программы. Консольный вывод будет таким:
Тип: <class 'datetime.datetime'>
Значение: 2024-12-20 21:37:59.112795
Репрезентация: datetime.datetime(2024, 12, 20, 21, 37, 59, 112795)
Тип: <class 'str'>
Значение: 2024-12-20 21:37:59.112795
Репрезентация: '2024-12-20 21:37:59.112795'Как видно, была создана обычная строковая переменная, содержащая текстовую информацию о дате и времени.
Каждый раз, когда необходимо получить строковое представление значения переменной или строковую репрезентацию переменной, вызываются методы __str__() и __repr__().
Так как они являются членами переменной, то сперва указывается ее имя, а уже потом через точку пишется название метода и скобки:
someVariable = "Здесь притаилась какая-то строка."
print(someVariable.__str__())
print(someVariable.__repr__())Такая запись выглядит некрасивой, нелаконичной и сложной. К тому же явное обращение к dunder-методам нельзя назвать хорошей практикой.
Поэтому в языке Python есть специальные функции, принимающие в качестве аргумента переменную и вызывающие ее методы __str__() или __repr__(). Они имеют похожие название — str() и repr() соответственно:
import datetime
dateNow = datetime.datetime.now()
print("Значение (__str__()):", dateNow.__str__())
print("Репрезентация (__repr__()):", dateNow.__repr__())
print("")
print("Значение (str()):", str(dateNow))
print("Репрезентация (repr()):", repr(dateNow))Консольный вывод этого примера будет таким:
Значение (__str__()): 2024-12-20 21:53:03.167788
Репрезентация (__repr__()): datetime.datetime(2024, 12, 20, 21, 53, 3, 167788)
Значение (str()): 2024-12-20 21:53:03.167788
Репрезентация (repr()): datetime.datetime(2024, 12, 20, 21, 53, 3, 167788)Как видно, результаты __str__() и str(), ровно как __repr__() и repr(), полностью совпадают.
Переопределение str() и repr()
В каждом новом классе, созданном пользователем, язык Python разрешает переопределять методы __str__() и __repr__(), получая уникальные значение и репрезентацию переменной.
При этом важно понимать, что в пользовательских классах Python автоматически не формирует возвращаемые значения этих методов:
class Car:
model = 'Tesla'
version = 2
def __init__(self, model, version):
self.model = model
self.version = version
someCar = Car("Cybertruck", 1)
print(str(someCar)) # значение переменной
print(repr(someCar)) # репрезентация переменнойКонсольный вывод этого примера будет таким:
<__main__.Car object at 0x7acf8c5c5580>
<__main__.Car object at 0x7acf8c5c5580>По умолчанию Python возвращает представление объекта во внутреннем формате. При этом, если в классе не определен метод __str__(), то вместо него вызывается __repr__().
Поэтому хорошей практикой будет переопределение методов __str__() и __repr__() для каждого нового пользовательского класса:
class Car:
model = 'Tesla'
version = 2
def __init__(self, model, version):
self.model = model
self.version = version
def __str__(self):
return str(self.model + " " + str(self.version) + ".0.0v")
def __repr__(self):
return "Car(" + repr(self.model) + ", " + str(self.version) + ")"
someCar = Car("Cybertruck", 1)
someOtherCar = eval(repr(someCar))
print(type(someCar))
print(type(someOtherCar))
print("")
print(str(someCar))
print(repr(someCar))
print("")
print(str(someOtherCar))
print(repr(someOtherCar))После запуска этого примера в консольном терминале появится вот такой вывод:
<class '__main__.Car'>
<class '__main__.Car'>
Cybertruck 1.0.0v
Car('Cybertruck', 1)
Cybertruck 1.0.0v
Car('Cybertruck', 1)Таким образом, благодаря переопределенным методам __str__() и __repr__() была создана точная копия оригинальной переменной пользовательского класса.**
**
Нужен сервер для практики? Арендуйте VPS/VDS в России — root-доступ, NVMe, DDoS-защита и поддержка 24/7.