Управление представлением объектов: self и print

Раздел: Основы Python -> Объектно-ориентированное программирование

Основной способ: переопределение метода __str__

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

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
    def __str__(self):
        return f"'{self.title}' by {self.author}"

book = Book("1984", "George Orwell")
print(book)

атрибуты класса python (атрибуты классов и объектов в python)

'1984' by George Orwell

библиотека классов python (библиотека классов в python)

Если метод __str__ не возвращает строку, возникает ошибка TypeError: __str__ returned non-string. Следует убедиться, что возвращается именно строковый объект. Ещё одна распространённая проблема - вызов print(self) внутри самого __str__, что приводит к бесконечной рекурсии. Лучше использовать только атрибуты self без повторного вызова print.

Альтернативные подходы

Как получить отладочное представление объекта?

Метод __repr__ предназначен для разработчиков и должен возвращать однозначное строковое представление, часто в виде кода, который можно использовать для воссоздания объекта. Если __str__ не определён, print использует __repr__.

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(10, 20)
print(p)

метод объекта python (методы объектов в python)

Point(10, 20)

Python структура объекта (структура объекта в python)

Основная ошибка - путаница между __repr__ и __str__. __repr__ должен быть максимально точным и удобным для разработчика, а __str__ - для пользователя. Если не определить ни один из них, print выведет что-то вроде <__main__.Book object at 0x...>.

Как вывести объект с красивым форматированием?

Внутри __str__ можно использовать f-строки, методы format или конкатенацию. F-строки - самый современный и читаемый способ прямо в методе.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    def __str__(self):
        return f"Сотрудник: {self.name}, зарплата: {self.salary:.2f} руб."

e = Employee("Иван", 75000.555)
print(e)

Python создание объектов (создание объектов в python)

Сотрудник: Иван, зарплата: 75000.56 руб.

Self object python (объект self в python)

Ошибка форматирования (например, ValueError: Unknown format code) случается при несоответствии типа и спецификатора. Следует проверять типы атрибутов.

Как быстро вывести все атрибуты объекта?

Использование словаря self.__dict__ внутри __str__ даёт простой способ отобразить все публичные атрибуты без явного перечисления.

class Config:
    def __init__(self, host, port, debug):
        self.host = host
        self.port = port
        self.debug = debug
    def __str__(self):
        return f"Config: {self.__dict__}"

cfg = Config("localhost", 8080, True)
print(cfg)

Object attribute python (атрибуты объекта в python)

Config: {'host': 'localhost', 'port': 8080, 'debug': True}

Python call method (вызов метода в python)

Словарь __dict__ не включает приватные атрибуты (с двойным подчёркиванием) без name mangling. Также следует помнить, что некоторые атрибуты могут быть не строковыми и их представление может быть неинформативным.

Как настраивать формат вывода с помощью format()?

Метод __format__ позволяет определить поведение строки формата для экземпляра класса. Это полезно, когда требуется разное представление в зависимости от спецификатора.

class Timed:
    def __init__(self, seconds):
        self.seconds = seconds
    def __format__(self, spec):
        if spec == "hours":
            return f"{self.seconds / 3600:.2f} ч"
        elif spec == "minutes":
            return f"{self.seconds / 60:.1f} мин"
        else:
            return f"{self.seconds} сек"

t = Timed(3665)
print(format(t, "hours"))
print(format(t, "minutes"))
print(format(t, ""))

Python класс данных (класс данных в python)

1.02 ч
61.1 мин
3665 сек

Class method python (методы классов в python)

Если __format__ не определён, используется __str__. При неверном спецификаторе ошибка не возникает, но стоит обрабатывать неизвестные спецификаторы.

Как не переопределять встроенные методы, а использовать отдельный метод?

Можно создать собственный метод, например show(), и вызывать его явно. Это оставляет print в стандартном виде, но даёт возможность кастомизированного вывода.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def show(self):
        return f"{self.name}, возраст {self.age}"

person = Person("Анна", 25)
print(person.show())

Python object methods (методы объектов в python)

Анна, возраст 25
Этот подход не влияет на поведение print(person). Если нужно единообразие, лучше всё же переопределить __str__.
- приватные атрибуты python (приватные атрибуты в python)
- создание типов python (создание пользовательских типов данных в python)
- тип данных класса python (тип данных класса в python)

Расширенные примеры и нестандартные ситуации

1. Совместное использование __str__ и __repr__ в иерархии классов

Пример
class Animal:
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return f"{self.__class__.__name__}(name='{self.name}')"
    def __str__(self):
        return f"Животное: {self.name}"

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed
    def __str__(self):
        return f"Собака {self.name} породы {self.breed}"

dog = Dog("Рекс", "овчарка")
print(dog)
print(repr(dog))
Собака Рекс породы овчарка
Dog(name='Рекс')

В дочернем классе переопределён только __str__, __repr__ наследуется от родителя. Это удобно для отладки и пользовательского вывода.

2. Использование dataclasses с автогенерацией __repr__

Пример
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float
    def __str__(self):
        return f"({self.x:.1f}, {self.y:.1f})"

p = Point(3.1415, 2.71828)
print(p)
print(repr(p))
(3.1, 2.7)
Point(x=3.1415, y=2.71828)

Декоратор @dataclass автоматически создаёт __repr__, а __str__ переопределён вручную для более краткого вывода.

3. Вывод объектов в коллекциях

Пример
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    def __repr__(self):
        return f"{self.name}: ${self.price:.2f}"

products = [Product("Мышь", 25.99), Product("Клавиатура", 59.49)]
print(products)
[Мышь: $25.99, Клавиатура: $59.49]

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

4. Условный вывод в зависимости от режима

Пример
class User:
    def __init__(self, username, email, admin=False):
        self.username = username
        self.email = email
        self.admin = admin
    def __str__(self):
        if self.admin:
            return f"[ADMIN] {self.username}"
        return f"{self.username} ({self.email})"

user1 = User("alice", "alice@example.com")
user2 = User("bob", "bob@example.com", admin=True)
print(user1)
print(user2)
alice (alice@example.com)
[ADMIN] bob

Такой подход позволяет изменять представление в зависимости от состояния объекта.

5. Использование __getattr__ и динамическое формирование строки

Пример
class Dynamic:
    def __init__(self, **kwargs):
        self.kwargs = kwargs
    def __getattr__(self, name):
        if name in self.kwargs:
            return self.kwargs[name]
        raise AttributeError
    def __str__(self):
        parts = [f"{k}={v}" for k, v in self.kwargs.items()]
        return " | ".join(parts)

d = Dynamic(color="red", size=42, enabled=True)
print(d)
color=red | size=42 | enabled=True

Здесь атрибуты создаются динамически, а __str__ собирает их в читаемую строку.

Использование self в print Python - comments

En
Python print self (python)