Классы и объекты

Что такое класс?

подробнее

Класс - это шаблон для создания объектов. Он определяет свойства (атрибуты) и поведение (методы), которые будут иметь объекты этого типа

Переменные в классе - его свойства (аттрибуты).
Функции в классе - его методы.
У любого класса есть конструктор - специальный метод, который вызывается при создании объекта.

Что такое экземпляр класса (объект)?

подробнее

Экземпляр класса (или объект) - это конкретная реализация класса, созданная на основе шаблона.

Жизненный цикл:

  • Определение класса — class MyClass:
  • Создание экземпляра — obj = MyClass()
  • Использование — работа с атрибутами и методами
  • Уничтожение — сборщик мусора удаляет объект, когда на него нет ссылок

Что такое Object и какие у него есть методы?

подробнее

Object — это базовый класс в Python, от которого наследуются все остальные классы. Это корень иерархии наследования в Python.

# Все классы неявно наследуются от object
class MyClass:  # эквивалентно class MyClass(object):
    pass

# Проверка
print(issubclass(MyClass, object))  # True
print(isinstance(MyClass(), object))  # True

Методы класса object:

  • Базовые методы
    • __new__(cls, ...) - Создает и возвращает новый экземпляр класса
    • __init__(self, ...) - Инициализирует новый экземпляр класса
    • __del__(self) - Деструктор — вызывается при уничтожении объекта
  • Методы представления
    • __str__(self) - Возвращает строковое представление объекта для пользователя
    • __repr__(self) - Возвращает строковое представление объекта для разработчиков
  • Методы сравнения
    • __eq__(self, other) - Определяет поведение оператора ==
    • __ne__(self, other) - Определяет поведение оператора !=
    • __lt__(self, other), __le__(self, other), __gt__(self, other), __ge__(self, other) - <, <=, >, >=
  • Методы арифметических операций
    • __add__(self, other), __sub__(self, other) - +, -
    • __mul__(self, other), __truediv__(self, other) - *, /
    • __floordiv__(self, other), __mod__(self, other), __pow__(self, other) - //, %, **
  • Методы доступа к атрибутам
    • __getattr__(self, name) - Вызывается, когда атрибут не найден
    • __setattr__(self, name, value) - Вызывается при установке любого атрибута
    • __delattr__(self, name) - Вызывается при удалении атрибута
  • Методы контейнеров
    • __len__(self) - Возвращает длину объекта
    • __getitem__(self, key) - Позволяет доступ по индексу/ключу
    • __setitem__(self, key, value) - Устанавливает значение по ключу
    • __delitem__(self, key) - Удаляет элемент по ключу
    • __contains__(self, item) - Определяет поведение оператора in
  • Методы вызова
    • __call__(self[, args...]) - Позволяет вызывать объект как функцию
  • Методы контекстного менеджера
    • __enter__(self) - Вызывается при входе в блок with
    • __exit__(self, exc_type, exc_value, traceback) - Вызывается при выходе из блока with
  • Полезные методы для интроспекции
    • __dir__(self) - Возвращает список атрибутов объекта
    • __dict__ - Словарь атрибутов экземпляра
    • __class__ - Возвращает класс объекта
    • __module__ - Возвращает имя модуля, в котором определен класс

Отличия атрибута класса от атрибута объекта, можно ли одинаковое имя сделать для них?

подробнее
  • Атрибут класса
    • Принадлежит классу, а не конкретному экземпляру
    • Общий для всех экземпляров класса
    • Определяется внутри класса, но вне методов
  • Атрибут объекта (экземпляра)
    • Принадлежит конкретному экземпляру класса
    • Уникален для каждого объекта
    • Определяется внутри методов, обычно в __init__

Можно ли использовать одинаковые имена?

Да, можно, но это может привести к неожиданному поведению:

class Example:
    x = 10  # Атрибут класса
    
    def __init__(self):
        self.x = 20  # Атрибут объекта с тем же именем

obj = Example()
print(obj.x)        # 20 (атрибут объекта)
print(Example.x)    # 10 (атрибут класса)

# Доступ к атрибуту класса через экземпляр
print(obj.__class__.x)  # 10
# Как работает доступ к атрибутам:
class Test:
    attr = "класс"  # Атрибут класса
    
    def __init__(self):
        self.attr = "объект"  # Атрибут объекта

obj = Test()

# Приоритет поиска атрибутов:
# 1. Атрибут объекта
# 2. Атрибут класса
# 3. Атрибуты родительских классов
# 4. AttributeError

print(obj.attr)  # "объект" - найден в объекте первым

Атрибуты класса подходят для:

class DatabaseConnection:
    # Константы
    DEFAULT_TIMEOUT = 30
    
    # Счетчики
    connection_count = 0
    
    # Общие настройки
    config = {"host": "localhost", "port": 5432}
    
    def __init__(self):
        DatabaseConnection.connection_count += 1
        self.connection_id = DatabaseConnection.connection_count

# Использование
print(DatabaseConnection.DEFAULT_TIMEOUT)  # 30
conn1 = DatabaseConnection()
conn2 = DatabaseConnection()
print(DatabaseConnection.connection_count)  # 2

Атрибуты объекта для:

class User:
    def __init__(self, username, email):
        # Уникальные данные каждого пользователя
        self.username = username
        self.email = email
        self.login_count = 0
        self.is_active = True

Проверка принадлежности атрибутов

class Test:
    class_attr = "класс"
    
    def __init__(self):
        self.instance_attr = "объект"

obj = Test()

# Проверка наличия атрибута
print(hasattr(obj, 'class_attr'))      # True
print(hasattr(obj, 'instance_attr'))   # True

# Получение атрибутов
print(getattr(obj, 'class_attr', 'default'))  # класс

# Проверка, где определен атрибут
print('class_attr' in obj.__dict__)      # False (в классе)
print('instance_attr' in obj.__dict__)   # True (в объекте)
print('class_attr' in Test.__dict__)     # True (в классе)

Лучшие практики

  • Избегайте одинаковых имен для атрибутов класса и объекта
  • Используйте атрибуты класса для общих данных и констант
  • Используйте атрибуты объекта для уникальных данных экземпляров
  • Будьте осторожны с изменяемыми атрибутами класса
  • Явно указывайте имя класса при работе с атрибутами класса

Как в классах хранятся атрибуты и методы? (__dict__)

подробнее

Аттрибуты в классах и объектах хранятся в словаре __dict__ в формате имя: значение
Методы хранятся в __dict__ класса, а не объекта