Основы классов в Python

Что такое класс (кратко)
Класс — это расширяемый фрагмент кода, описывающий структуру и поведение объектов одного типа. Объект класса — это конкретный экземпляр с собственным состоянием.
Ключевые варианты поиска и намерения: классы Python, self в Python, инкапсуляция, методы доступа, dataclass.
Основные понятия
- Шаблон: класс описывает поля (атрибуты) и методы (поведение).
- Экземпляр: объект, созданный по шаблону класса.
- Атрибуты: переменные состояния экземпляра.
- Методы: функции, связанные с экземпляром.
Коротко: self — ссылка на текущий экземпляр (автоматически передаётся при обращении к методу). Конструктор (init) инициализирует состояние.
Пример класса (Car)
Ниже — исходный пример класса, как он был в оригинальном материале. Он демонстрирует базовую структуру, но содержит ряд недостатков (о которых мы расскажем дальше).
class Car:
#Constructor
#the init method is responsible for initialising the values of the instance #variables in the class.
def __init__(self, car_name, driver_name, license_plate_number,
current_speed, speeding_charge, colour):
self.car_name = car_name
self._driver_name = driver_name
self._license_plate_number = license_plate_number
self._current_speed = current_speed
self._speeding_charge = speeding_charge
self._colour = colour
#Accessor methods
#The get_car_name method returns the name of the car
def get_car_name(self):
return self._car_name
#The get_driver_name method returns the name of the driver
def get_driver_name(self):
return self._driver_name
#The get_license_plate method returns the license plate of the car
def get_license_plate(self):
return self._license_plate
#The get_current_speed method returns the current speed at which the car is #moving
def get_current_speed(self):
return self._current_speed
#The get_speeding_charge method returns the total amount of money that the
#driver has to pay for over-speeding
def get_speeding_charge(self):
return self._speeding_charge
#The get_colour method returns the colour of our sweet ride!
def get_colour(self):
return self._colour
#Mutator methods
#The set_driver value changes the driver of our car.
def set_driver(self, new_driver):
self._driver_name = new_driver
#The speeding_ticket method ensures that the driver is charged 50 bucks when#he or she caught speeding above 140 miles per hour :(
def speeding_ticket(self, current_speed):
if current_speed <= 140:
return False
else:
self._speeding_charge += 50
return True
#The make_payment method ensures that the driver is able to pay for all the #amount that he or she owes for speeding ticket charges.
def make_payment(self, amount_paid):
self._speeding_charge -= amount_paid
Комментарии к примеру
- Этот класс моделирует автомобиль с состоянием: имя автомобиля, имя водителя, номер, текущая скорость, штрафы, цвет.
- Логика штрафа: если скорость превышает 140 миль/ч, к счёту добавляется $50. 140 миль/ч ≈ 225 км/ч.
- В коде есть опечатки и отсутствие проверок типов и диапазонов (это потенциальный источник ошибок).
Разбор ключевых частей
Self
self — это ссылка на конкретный экземпляр. При вызове instance.get_colour() интерпретатор автоматически передаёт объект как первый аргумент, поэтому программист не указывает self вручную.
Конструктор
init создаёт и инициализирует состояние нового объекта. Пример использования:
# создание экземпляра
Car("Bugatti", "David Sasu", 90828, 0, 0, "Cherry Red")В нашем примере объект состоит из атрибутов: _car_name, _driver_name, _license_plate, _current_speed, _speeding_charge, _colour.
Аксессоры (Accessor methods)
Аксессоры возвращают состояние экземпляра (get_car_name, get_driver_name, get_license_plate, get_current_speed, get_speeding_charge, get_colour).
Мутаторы (Mutator methods)
Мутаторы изменяют состояние (set_driver, speeding_ticket, make_payment).
Инкапсуляция
Инкапсуляция — принцип ООП, предполагающий скрытие внутренней реализации и предоставление ограниченного интерфейса. В Python принято использовать соглашение с одним подчёркиванием _attr для обозначения защищённых атрибутов.
Почему исходная реализация ненадёжна
- Нет проверки типов аргументов (строка вместо числа может вызвать исключение).
- Ошибки имён: в getcarname возвращается self._car_name, тогда как в __init задано self.car_name — логическая ошибка.
- Нет ограничений на значения (скорость может быть отрицательной, плата — отрицательной и т. д.).
- Метод speeding_ticket принимает current_speed, но класс уже хранит _current_speed — дублирование информации.
Важно: 140 миль/ч — очень большой порог; если ваша бизнес-логика использует другие единицы, явно документируйте это и приводите значения в международных единицах.
Важно: всегда валидируйте входные данные и придерживайтесь единообразия имён атрибутов.
Улучшенная версия класса — шаблон и паттерны
Ниже — улучшенный пример на Python 3 с аннотациями типов, свойствами и валидацией. Этот код демонстрирует лучшие практики: единый источник правды для скорости, проверку типов и использование свойств для доступа.
from typing import Union
class Car:
"""Простой и безопасный класс автомобиля."""
SPEED_LIMIT_MPH = 140 # порог для штрафа в милях в час
SPEEDING_FINE_USD = 50 # сумма штрафа в долларах
def __init__(self, car_name: str, driver_name: str, license_plate_number: Union[str,int],
current_speed: float = 0.0, speeding_charge: float = 0.0, colour: str = "") -> None:
if not isinstance(car_name, str):
raise TypeError("car_name must be a string")
if not isinstance(driver_name, str):
raise TypeError("driver_name must be a string")
if not isinstance(current_speed, (int, float)):
raise TypeError("current_speed must be a number")
if current_speed < 0:
raise ValueError("current_speed cannot be negative")
if speeding_charge < 0:
raise ValueError("speeding_charge cannot be negative")
self._car_name = car_name
self._driver_name = driver_name
self._license_plate = str(license_plate_number)
self._current_speed = float(current_speed)
self._speeding_charge = float(speeding_charge)
self._colour = colour
# свойства (безопасный доступ)
@property
def car_name(self) -> str:
return self._car_name
@property
def driver_name(self) -> str:
return self._driver_name
@driver_name.setter
def driver_name(self, new_driver: str) -> None:
if not isinstance(new_driver, str):
raise TypeError("driver_name must be a string")
self._driver_name = new_driver
@property
def current_speed(self) -> float:
return self._current_speed
@current_speed.setter
def current_speed(self, speed: float) -> None:
if not isinstance(speed, (int, float)):
raise TypeError("speed must be a number")
if speed < 0:
raise ValueError("speed cannot be negative")
self._current_speed = float(speed)
@property
def speeding_charge(self) -> float:
return self._speeding_charge
def apply_speeding_ticket(self) -> bool:
"""Применяет штраф, если текущая скорость больше порога. Возвращает True, если добавили штраф."""
if self._current_speed > self.SPEED_LIMIT_MPH:
self._speeding_charge += self.SPEEDING_FINE_USD
return True
return False
def make_payment(self, amount_paid: float) -> None:
if not isinstance(amount_paid, (int, float)):
raise TypeError("amount_paid must be a number")
if amount_paid < 0:
raise ValueError("amount_paid cannot be negative")
self._speeding_charge = max(0.0, self._speeding_charge - float(amount_paid))
def __str__(self) -> str:
return f"{self._car_name} ({self._license_plate}) — водитель: {self._driver_name}, скорость: {self._current_speed} миль/ч"Пояснения:
- Использованы свойства (property) вместо явных get/set методов — это более «питоничный» стиль.
- Аннотации типов помогают инструментам статической проверки и IDE.
- Валидация предотвращает неожиданное поведение.
Альтернативные подходы
- Dataclasses: когда нужен простой контейнер данных, используйте @dataclass для автоматической генерации init, repr и сравнения.
- namedtuple / typing.NamedTuple: для неизменяемых структур данных.
- Функциональный подход: если нет необходимости в состоянии, используйте чистые функции и структуры данных.
Пример dataclass для простых случаев:
from dataclasses import dataclass
@dataclass
class SimpleCar:
car_name: str
driver_name: str
license_plate: str
current_speed: float = 0.0
speeding_charge: float = 0.0
colour: str = ""Когда классы — плохой выбор
- Если вам нужно только хранить набор полей без поведения — проще использовать dataclass или словарь.
- Маленькие утилиты и одноразовые преобразования проще сделать функциями.
- Чрезмерное применение ООП может усложнять код (анти-паттерн: «анемичная модель» — класс только с геттерами/сеттерами и логикой вне класса).
Ментальные модели и эвристики
- Класс = чертёж; экземпляр = дом, построенный по чертежу.
- «Единый источник правды»: не дублируйте данные в объекте (например, current_speed и current_speed в аргументах методов).
- Правило YAGNI: не добавляйте сложные механизмы, пока они реально не нужны.
Критерии приёмки
- Конструктор создаёт объект с указанными атрибутами и корректными типами.
- Установка current_speed < 0 вызывает ValueError.
- apply_speeding_ticket добавляет штраф только если скорость > 140 миль/ч.
- make_payment снижает speeding_charge и никогда не делает её отрицательной.
- Строковое представление (str) содержит имя автомобиля и номер.
Мини‑методология при разработке класса
- Определите состояние (список полей).
- Опишите требуемое поведение (методы).
- Выберите представление (dataclass vs класс с логикой).
- Напишите тесты для граничных случаев.
- Добавьте валидацию и документацию.
Тесты и случаи приёмки (примеры)
- Создать Car(“A”, “B”, “123”, 0, 0, “red”) — ожидается успешное создание.
- Установить current_speed = -10 — ожидается ValueError.
- Для скорости 141 вызвать apply_speeding_ticket — speeding_charge увеличится на 50.
- Оплатить сумму больше текущей — speeding_charge станет 0.
Чек‑лист по ролям
- Новичок:
- Могу создать экземпляр класса.
- Понимаю, что такое self и init.
- Ревьювер кода:
- Проверить валидацию типов и диапазонов.
- Убедиться, что нет дублирования состояния.
- Поддерживающий инженер:
- Добавить тесты для пограничных случаев.
- Документировать единицы измерения и валюту.
Краткий словарь (1‑строчник)
- Класс: шаблон для объектов.
- Экземпляр: объект, созданный по классу.
- self: ссылка на текущий экземпляр.
- Конструктор (init): инициализирует новый объект.
- Аксессор/геттер: возвращает значение атрибута.
- Мутатор/сеттер: изменяет значение атрибута.
- Инкапсуляция: скрытие внутренней реализации.
Факт‑бокс
- Порог штрафа в примере: 140 миль/ч ≈ 225 км/ч.
- Штраф в примере: $50 (USD).
- Рекомендуемая практика: использовать аннотации типов и свойства для публичного API класса.
Заключение
Вы изучили основные концепции классов в Python: что такое self, как работает конструктор, чем отличаются аксессоры и мутаторы, и почему важна инкапсуляция и валидация. Практикуйтесь с небольшими примерами, затем переводите успешные шаблоны в более сложные системы.
Краткое резюме:
- Класс — это шаблон для создания объектов.
- В Python предпочтительнее свойства и аннотации типов.
- Всегда валидируйте входные данные и пишите тесты.
Похожие материалы
Несколько аккаунтов Skype: Multi Skype Launcher
Журнал для работы: повысить продуктивность
Персональные звуки уведомлений на Android
Скачивание шоу Hulu для офлайн‑просмотра
Microsoft Start: персонализированная новостная лента