Ответ
Python — полностью объектно-ориентированный язык, и его синтаксис и семантика предоставляют элегантные способы реализации четырех основных принципов ООП.
1. Инкапсуляция (Encapsulation) Сокрытие внутреннего состояния объекта и предоставление контролируемого доступа через публичные методы. В Python это соглашение, а не строгое правило.
class TemperatureSensor:
def __init__(self):
# Защищенный атрибут (соглашение: один underscore)
self._current_temperature = 0.0
# Приватный атрибут (name mangling: два underscore)
self.__calibration_factor = 1.02
# Публичный метод-геттер для контролируемого доступа
def get_temperature(self):
# Внутренняя логика, например, чтение с датчика и калибровка
raw_reading = self._read_hardware()
self._current_temperature = raw_reading * self.__calibration_factor
return self._current_temperature
# Защищенный метод для внутреннего использования
def _read_hardware(self):
# Симуляция чтения с аппаратного датчика
return 25.5
# Публичный метод-сеттер с валидацией
def set_calibration(self, factor):
if 0.5 < factor < 1.5:
self.__calibration_factor = factor
else:
raise ValueError("Calibration factor out of range")
sensor = TemperatureSensor()
print(sensor.get_temperature()) # OK: 26.01
# print(sensor.__calibration_factor) # AttributeError
print(sensor._TemperatureSensor__calibration_factor) # Доступ с name mangling (но не нужно так делать)
sensor.set_calibration(1.1) # OK
2. Наследование (Inheritance) Создание нового класса на основе существующего с возможностью переопределения или расширения функциональности. Python поддерживает множественное наследование.
class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model
self._speed = 0
def move(self):
return f"The {self.make} {self.model} is moving."
class ElectricCar(Vehicle): # ElectricCar наследует от Vehicle
def __init__(self, make, model, battery_capacity):
# Вызов конструктора родительского класса
super().__init__(make, model)
self.battery_capacity = battery_capacity
self._charge_level = 100
# Переопределение метода родителя
def move(self):
if self._charge_level > 0:
self._charge_level -= 5
return f"The {self.make} {self.model} moves silently. Charge: {self._charge_level}%"
else:
return "Battery depleted!"
# Новый метод, специфичный для дочернего класса
def recharge(self):
self._charge_level = 100
my_tesla = ElectricCar("Tesla", "Model S", 100)
print(my_tesla.move()) # Вызов переопределенного метода
print(isinstance(my_tesla, Vehicle)) # True
print(issubclass(ElectricCar, Vehicle)) # True
3. Полиморфизм (Polymorphism) Возможность объектов с разной внутренней структурой иметь одинаковый интерфейс. В Python это реализуется через утиную типизацию (duck typing).
class PDFExporter:
def export(self, data):
# Логика генерации PDF
return "PDF file generated"
class CSVExporter:
def export(self, data):
# Логика генерации CSV
return "CSV file generated"
class ReportGenerator:
def generate_report(self, data, exporter): # exporter — любой объект с методом export()
# Полиморфный вызов: не важно, какой именно экспортер,
# главное, что у него есть метод export.
result = exporter.export(data)
print(result)
# Использование
generator = ReportGenerator()
data = {"sales": [100, 200, 150]}
generator.generate_report(data, PDFExporter()) # Выведет: PDF file generated
generator.generate_report(data, CSVExporter()) # Выведет: CSV file generated
4. Абстракция (Abstraction) Сокрытие сложной реализации и предоставление простого интерфейса. Достигается через абстрактные базовые классы (ABC).
from abc import ABC, abstractmethod
import numpy as np
class MLModel(ABC):
"""Абстракция модели машинного обучения."""
@abstractmethod
def fit(self, X, y):
"""Обучить модель на данных X, y."""
pass
@abstractmethod
def predict(self, X):
"""Сделать предсказание для данных X."""
pass
# Абстрактное свойство
@property
@abstractmethod
def is_fitted(self):
pass
class LinearRegressionModel(MLModel):
"""Конкретная реализация абстракции."""
def __init__(self):
self._coef = None
self._intercept = None
self._fitted = False
def fit(self, X, y):
# Реальная, сложная реализация метода fit
X_with_intercept = np.c_[np.ones(X.shape[0]), X]
coeffs = np.linalg.pinv(X_with_intercept.T @ X_with_intercept) @ X_with_intercept.T @ y
self._intercept, *self._coef = coeffs
self._fitted = True
def predict(self, X):
if not self._fitted:
raise ValueError("Model is not fitted yet.")
return self._intercept + X @ self._coef
@property
def is_fitted(self):
return self._fitted
# Пользователь работает с абстракцией, не зная деталей реализации.
model = LinearRegressionModel()
model.fit(X_train, y_train)
predictions = model.predict(X_test)
Эти принципы в Python реализованы гибко, что позволяет писать выразительный и эффективный объектно-ориентированный код.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши объекты ориентации. Слушай, давай я тебе на пальцах объясню, как это в Питоне работает, а то в учебниках пишут такое, что волнение ебать, терпения ноль ебать.
1. Инкапсуляция, или "Спрячь своё говно" Это когда ты делаешь вид, что у тебя внутри всё чисто и красиво, а на самом деле там бардак, но ты его прикрыл. В Питоне это вообще ебушки-воробушки — строгости ноль, всё на доверии. Один подчёркивание — типа "не лезь, а то обидно будет". Два подчёркивания — "ну ты ваще охренел что ли, зачем тебе туда?" Но если очень хочется — залезешь, конечно.
class ДатчикТемпературы:
def __init__(self):
self._текущая_температура = 0.0 # Ну типа не трогай, ладно?
self.__коэффициент_поправки = 1.02 # Вообще отъебись, это приватное!
def дай_температуру(self):
# А внутри-то какая-то магия, калибровка, херня
сырое_значение = self._прочитай_железку()
self._текущая_температура = сырое_значение * self.__коэффициент_поправки
return self._текущая_температура
def _прочитай_железку(self):
return 25.5 # Просто с потолка взял, да похуй
датчик = ДатчикТемпературы()
print(датчик.дай_температуру()) # Всё ок, работает
# print(датчик.__коэффициент_поправки) # А вот тут тебе AttributeError в морду
# Но если упоротый — print(датчик._ДатчикТемпературы__коэффициент_поправки) — и всё, ты в душу бога мать полез.
2. Наследование, или "Папины гены" Это когда ты берёшь чужой класс и говоришь: "Всё твоё теперь моё, а ещё я тут своё прикручу". В Питоне можно наследоваться хоть от десяти классов сразу — ёперный театр, конечно, но можно.
class Транспорт:
def __init__(self, марка, модель):
self.марка = марка
self.модель = модель
self._скорость = 0
def поехали(self):
return f"{self.марка} {self.модель} поехал."
class Электромобиль(Транспорт): # Смотри-ка, сынок у папы
def __init__(self, марка, модель, ёмкость_батареи):
super().__init__(марка, модель) # Папины гены подтянули
self.ёмкость_батареи = ёмкость_батареи
self._заряд = 100
# А вот тут сынок говорит: "Папа, твой метод — говно, я сделаю по-своему"
def поехали(self):
if self._заряд > 0:
self._заряд -= 5
return f"{self.марка} {self.модель} едет тихо. Заряд: {self._заряд}%"
else:
return "Батарея сдохла!"
тесла = Электромобиль("Tesla", "Model S", 100)
print(тесла.поехали()) # Работает переопределённый метод
print(isinstance(тесла, Транспорт)) # True, он и правда сынок
3. Полиморфизм, или "Если крякает как утка..."
Это вообще любимая фишка Питона. Нам похуй, что это за объект. Главное, чтобы у него был нужный метод. Утка? Селезень? Хуй с горы? Если у него есть метод .export() — значит, он экспортёр, и мы его используем.
class PDFВыгрузчик:
def export(self, данные):
return "PDF-файл готов"
class CSVВыгрузчик:
def export(self, данные):
return "CSV-файл готов"
class ГенераторОтчётов:
def сделай_отчёт(self, данные, выгрузчик): # Нам всё равно, что за выгрузчик
результат = выгрузчик.export(данные) # Главное, чтобы export был!
print(результат)
генератор = ГенераторОтчётов()
данные = {"продажи": [100, 200, 150]}
генератор.сделай_отчёт(данные, PDFВыгрузчик()) # PDF-файл готов
генератор.сделай_отчёт(данные, CSVВыгрузчик()) # CSV-файл готов
# Сюда можно хоть ХуйВПальто подсунуть, если у него export() есть — будет работать.
4. Абстракция, или "Обещания, которые надо выполнить" Это когда ты говоришь: "Вот тебе план, детали — твои проблемы". Абстрактный класс — это такой начальник, который говорит "сделай!", но сам нихуя не делает. А ты обязан реализовать.
from abc import ABC, abstractmethod
import numpy as np
class МодельМО(ABC):
"""Ну типа модель машинного обучения, абстрактная такая."""
@abstractmethod
def обучи(self, X, y):
"""Обучи модель. Как — твои трудности."""
pass
@abstractmethod
def предскажи(self, X):
"""Сделай предсказание. И чтобы работало."""
pass
@property
@abstractmethod
def обучена_ли(self):
pass
class ЛинейнаяРегрессия(МодельМО):
"""А вот тут уже конкретика, без абстракций."""
def __init__(self):
self._коэфф = None
self._пересечение = None
self._обучена = False
def обучи(self, X, y):
# А вот тут реальная, сложная математика, овердохуища формул
X_с_единицей = np.c_[np.ones(X.shape[0]), X]
коэффициенты = np.linalg.pinv(X_с_единицей.T @ X_с_единицей) @ X_с_единицей.T @ y
self._пересечение, *self._коэфф = коэффициенты
self._обучена = True # Ура, научилась!
def предскажи(self, X):
if not self._обучена:
raise ValueError("Модель ещё не обучена, иди учи!")
return self._пересечение + X @ self._коэфф
@property
def обучена_ли(self):
return self._обучена
# А пользователь вообще в душе не ебёт, как там внутри. Ему главное — predict.
модель = ЛинейнаяРегрессия()
модель.обучи(X_тренировка, y_тренировка)
предсказания = модель.предскажи(X_тест)
Вот и вся магия. В Питоне это не железные правила, а так, рекомендации. Хочешь — соблюдай, хочешь — делай как знаешь. Но если хочешь, чтобы твой код не выглядел как пиздопроебибна, лучше придерживайся.