Специальные (Магические) Методы

В Python существуют методы со специальными именами, которые начинаются и заканчиваются двойным подчеркиванием (например, __init__, __str__). Их называют магическими или dunder (double underscore) методами.

Они не предназначены для прямого вызова (обычно вы не пишете my_object.__str__()). Вместо этого Python вызывает их автоматически в ответ на определенные операции или встроенные функции. Они позволяют настраивать поведение объектов вашего класса, чтобы они работали с операторами и функциями Python ожидаемым образом.

Основные Специальные Методы:

  • __init__(self, ...): Инициализатор объекта (конструктор). Вызывается при создании экземпляра (MyClass(...)).
  • __str__(self): Возвращает неформальное, удобочитаемое строковое представление объекта. Вызывается функцией print() и str(). Если не определен, print() использует __repr__.
  • __repr__(self): Возвращает официальное, однозначное строковое представление объекта, которое в идеале позволяет воссоздать объект (eval(repr(obj)) == obj). Вызывается, когда объект отображается в интерактивной консоли, а также функцией repr(). Если __str__ не определен, используется __repr__. Рекомендуется всегда определять __repr__.
  • __len__(self): Возвращает “длину” объекта. Вызывается функцией len().
  • __add__(self, other): Позволяет использовать оператор + с объектами (obj1 + obj2).
  • __sub__(self, other): Оператор -.
  • __mul__(self, other): Оператор *.
  • __eq__(self, other): Позволяет использовать оператор сравнения - (obj1 == obj2).
  • __lt__(self, other): Оператор < (меньше чем).
  • __le__(self, other): Оператор <= (меньше или равно).
  • __gt__(self, other): Оператор > (больше чем).
  • __ge__(self, other): Оператор >= (больше или равно).
  • __getitem__(self, key): Позволяет обращаться к элементам объекта по индексу или ключу (obj[key]).
  • __setitem__(self, key, value): Позволяет присваивать значения элементам по индексу или ключу (obj[key] = value).
  • __delitem__(self, key): Позволяет удалять элементы по индексу или ключу (del obj[key]).
  • __call__(self, ...): Позволяет “вызывать” объект как функцию (obj(...)).
class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
 
    # Неформальное представление для пользователя
    def __str__(self):
        return f'"{self.title}" автора {self.author}'
 
    # Официальное представление для разработчика
    def __repr__(self):
        return f"Book(title='{self.title}', author='{self.author}', pages={self.pages})"
 
    # Длина книги - количество страниц
    def __len__(self):
        return self.pages
 
    # Сравнение книг по количеству страниц
    def __eq__(self, other):
        if isinstance(other, Book):
            return self.pages == other.pages
        return NotImplemented
 
    def __lt__(self, other):
        if isinstance(other, Book):
            return self.pages < other.pages
        return NotImplemented
 
book1 = Book("Война и Мир", "Л. Толстой", 1225)
book2 = Book("Гарри Поттер", "Дж. Роулинг", 400)
 
print(book1)         # Вызывает __str__: "Война и Мир" автора Л. Толстой
print(str(book1))    # Вызывает __str__: "Война и Мир" автора Л. Толстой
print(repr(book1))   # Вызывает __repr__: Book(title='Война и Мир', author='Л. Толстой', pages=1225)
print(len(book1))    # Вызывает __len__: 1225
 
print(book1 == book2) # False (__eq__)
print(book1 > book2)  # True (__lt__ определяет и > через отражение)

7.2. Свойства (@property)

Декоратор @property позволяет определить метод, который будет доступен как атрибут (без вызова через скобки ()). Это основной “питоничный” способ реализации геттеров, сеттеров и делитеров, позволяющий контролировать доступ к атрибутам и добавлять логику при их чтении, записи или удалении.

  • Геттер (@property): Метод, декорированный @property, выполняется при чтении атрибута.
  • Сеттер (@attribute_name.setter): Метод, декорированный сеттером свойства, выполняется при попытке присвоить значение атрибуту. Позволяет добавить валидацию.
  • Делитер (@attribute_name.deleter): Метод, декорированный делитером свойства, выполняется при попытке удалить атрибут (del obj.attribute).
class Product:
    def __init__(self, name, price):
        self.name = name
        # Используем сеттер при инициализации для валидации
        self.price = price
 
    @property
    def price(self):
        # Этот метод вызывается при чтении obj.price
        print(f"Получение цены для {self.name}")
        return self._price # Возвращаем значение из "приватного" атрибута
 
    @price.setter
    def price(self, value):
        # Этот метод вызывается при присваивании obj.price = value
        print(f"Установка цены {value} для {self.name}")
        if value < 0:
            raise ValueError("Цена не может быть отрицательной")
        # Сохраняем значение в "приватном" атрибуте (с подчеркиванием)
        self._price = value
 
    @price.deleter
    def price(self):
        # Этот метод вызывается при del obj.price
        print(f"Удаление цены для {self.name}")
        del self._price
 
# Создаем объект, сеттер вызывается из __init__
item = Product("Ноутбук", 50000)
# Вывод: Установка цены 50000 для Ноутбук
 
# Читаем цену - вызывается геттер (@property)
current_price = item.price
# Вывод: Получение цены для Ноутбук
print(f"Текущая цена: {current_price}") # 50000
 
# Изменяем цену - вызывается сеттер (@price.setter)
item.price = 55000
# Вывод: Установка цены 55000 для Ноутбук
 
# Попытка установить невалидную цену
try:
    item.price = -100
except ValueError as e:
    print(e) # Вывод: Цена не может быть отрицательной
 
# Удаляем цену - вызывается делитер (@price.deleter)
del item.price
# Вывод: Удаление цены для Ноутбук
 
# print(item.price) # AttributeError: _price (атрибут был удален)

Использование свойств позволяет сохранить простой синтаксис доступа к атрибутам (obj.price), но при этом добавить сложную логику или валидацию при необходимости. Имя “приватного” атрибута (_price) обычно начинается с одного подчеркивания.

〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰