Отображение класса при выводе данных
Вывод класса в Python
Эффективное решение: переопределение метода __str__
Для получения понятного и читаемого вывода экземпляра пользовательского класса при использовании функции print() или преобразования в строку (str()) следует переопределить специальный метод __str__. Этот метод должен возвращать строку, содержащую информацию, которую разработчик хочет увидеть при выводе объекта.
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
def __str__(self):
return f"'{self.title}' by {self.author}, published in {self.year}"
book = Book("1984", "George Orwell", 1949)
print(book)Python вывод (вывод данных на экран в python)
'1984' by George Orwell, published in 1949
вывод класса python (вывод класса в python)
Без переопределения __str__ вывод был бы неинформативным: <__main__.Book object at 0x...>. Переопределение метода решает эту проблему и делает отладку и логирование удобнее.
Типичные ошибки:
- Метод __str__ возвращает не строку, а другой тип (например, число) - возникает ошибка TypeError.
- Если внутри __str__ происходит обращение к атрибуту, который не определён, возникает AttributeError. Рекомендуется проверять наличие атрибутов через getattr() или гарантировать их существование.
- При циклических ссылках (объект A содержит ссылку на B, а B - на A) вызов __str__ может привести к бесконечной рекурсии. Решение - ограничить глубину вывода или использовать специализированные инструменты, такие как reprlib.
Как получить отладочное представление объекта?
Для разработчиков, нуждающихся в однозначном представлении, которое, по возможности, можно использовать для восстановления объекта, переопределяется метод __repr__. В отличие от __str__, он должен содержать всю информацию, необходимую для создания копии объекта.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
p = Point(3, 4)
print(repr(p)) # Вывод через repr()
print(p) # По умолчанию вызовет __repr__, если __str__ не определёнформаты вывода python (форматы вывода в python)
Point(3, 4) Point(3, 4)
Проблема: Если __repr__ не возвращает строку, которую можно интерпретировать как корректное выражение Python, теряется цель отладочного представления. Желательно, чтобы строка была валидным кодом для воссоздания объекта.
Как вывести только отдельные атрибуты без изменения класса?
Если нет возможности изменить класс (например, библиотечный класс), можно использовать f-строки или метод format() при выводе, обращаясь к атрибутам напрямую.
class Car:
def __init__(self, model, year):
self.model = model
self.year = year
car = Car("Tesla Model 3", 2023)
print(f"Модель: {car.model}, год: {car.year}")
print("Модель: {0.model}, год: {0.year}".format(car))
Модель: Tesla Model 3, год: 2023 Модель: Tesla Model 3, год: 2023
Ошибка: Если атрибут отсутствует, возникнет AttributeError. Рекомендуется использовать getattr() с значением по умолчанию.
Как настроить формат вывода с помощью format() и спецификаторов?
Метод __format__ позволяет определить, как объект будет отображаться при использовании f-строк, format() и даже встроенного str.format().
import math
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __format__(self, spec):
if spec == 'polar':
r = math.hypot(self.x, self.y)
phi = math.atan2(self.y, self.x)
return f'({r:.2f} ∠ {phi:.2f} rad)'
return f'(x={self.x}, y={self.y})'
v = Vector(3, 4)
print(f'{v}')
print(f'{v:polar}')
(x=3, y=4) (5.00 ∠ 0.93 rad)
Если спецификатор формата не распознан, можно вернуть стандартное представление через str(self) или repr(self).
Как узнать тип (класс) объекта при отладке?
Функция type() возвращает объект класса, который можно вывести как строку. Чаще всего это используется для логирования или проверки типа.
class Dog:
pass
d = Dog()
print(type(d))
print(type(d).__name__)
<class '__main__.Dog'> Dog
Сравнение с isinstance() даёт логический результат, а type() - сам класс. Это может быть неочевидно для новичков.
Как получить полный список атрибутов объекта и их значений?
Функция vars() (без аргументов - локальные переменные, с объектом - его словарь __dict__) и dir() (список имён) помогают вывести содержимое объекта.
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
e = Employee("Alice", 60000)
print(vars(e))
print([attr for attr in dir(e) if not attr.startswith('_')])
{'name': 'Alice', 'salary': 60000}
['name', 'salary']
vars() не включает унаследованные атрибуты и свойства. dir() возвращает много служебных методов. Для вывода только пользовательских атрибутов требуется фильтрация.
Как быстро получить читаемый вывод для классов-контейнеров?
Декоратор @dataclass из модуля dataclasses автоматически генерирует методы __init__, __repr__ (и другие), что избавляет от ручного написания кода для вывода.
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
active: bool = True
u = User(1, "John", False)
print(u)
print(repr(u))
User(id=1, name='John', active=False) User(id=1, name='John', active=False)
По умолчанию dataclass генерирует __repr__, но не __str__ (если не задан order=True или не переопределён). Можно передать repr=False, если вывод не нужен.
Расширенные примеры вывода класса
Кастомный __str__ с форматированием и обработкой None
class Contact:
def __init__(self, name, phone, email=None):
self.name = name
self.phone = phone
self.email = email
def __str__(self):
parts = [f"{self.name}: phone {self.phone}"]
if self.email:
parts.append(f", email {self.email}")
return ''.join(parts)
c = Contact("Anna", "+7-123-456-78-90")
print(c)
c2 = Contact("Bob", "+1-555-1234", "bob@example.com")
print(c2)
Anna: phone +7-123-456-78-90 Bob: phone +1-555-1234, email bob@example.com
__repr__ с возможностью eval (восстановление объекта)
class Fraction:
def __init__(self, numerator, denominator):
self.numer = numerator
self.denom = denominator
def __repr__(self):
return f'Fraction({self.numer}, {self.denom})'
f = Fraction(3, 4)
f_repr = repr(f)
print(f_repr)
f_new = eval(f_repr) # Восстанавливаем объект
print(f_new.numer, f_new.denom)
Fraction(3, 4) 3 4
Внимание: eval() может быть опасен, если строка получена из ненадёжного источника. В отладочных целях - допустимо.
Переопределение __format__ для сложной логики форматирования
from datetime import datetime
class Event:
def __init__(self, name, date):
self.name = name
self.date = date # datetime объект
def __format__(self, spec):
if spec == 'short':
return f'{self.name} ({self.date:%d.%m.%Y})'
return f'Event "{self.name}" on {self.date:%Y-%m-%d %H:%M}'
e = Event("Conference", datetime(2025, 3, 15, 9, 0))
print(f'{e}')
print(f'{e:short}')
Event "Conference" on 2025-03-15 09:00 Conference (15.03.2025)
Использование dataclass с дополнительными методами вывода
from dataclasses import dataclass, field
@dataclass
class Employee:
name: str
position: str
salary: float = field(repr=False) # скрыть зарплату в repr
def __str__(self):
return f'{self.name} ({self.position})'
e = Employee("Eve", "Engineer", 85000)
print(e)
print(repr(e))
Eve (Engineer) Employee(name='Eve', position='Engineer')
Вывод с помощью модуля pprint для сложных вложенных объектов
from pprint import pprint
class Node:
def __init__(self, value, children=None):
self.value = value
self.children = children or []
def __repr__(self):
return f'Node({self.value!r}, {self.children!r})'
tree = Node(1, [Node(2, [Node(4), Node(5)]), Node(3)])
pprint(tree)
Node(1, [Node(2, [Node(4, []), Node(5, [])]), Node(3, [])])
Избегание рекурсии при выводе объектов с циклическими ссылками
import reprlib
class LinkedListNode:
def __init__(self, value, next_node=None):
self.value = value
self.next = next_node
def __repr__(self):
return f'LinkedListNode({self.value!r})'
# Создаём цикл: node3 -> node1
node1 = LinkedListNode(1)
node2 = LinkedListNode(2, node1)
node3 = LinkedListNode(3, node2)
node1.next = node3
# reprlib.Repr позволяет ограничить глубину
r = reprlib.Repr()
r.maxlevel = 3
print(r.repr(node1))
LinkedListNode(1)
Если использовать стандартный repr(), будет RecursionError. reprlib помогает безопасно вывести объект.