Как создать абстрактный класс?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как создать абстрактный класс
Абстрактный класс — это класс, который не может быть инстанцирован (создана переменная с его типом), а служит шаблоном для наследующих его классов. В Python для создания абстрактных классов используется модуль abc (Abstract Base Classes).
Базовый синтаксис
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass
@abstractmethod
def move(self):
pass
# Обычный метод (не абстрактный)
def description(self):
return f"I am a {self.__class__.__name__}"
# Попытка создать экземпляр абстрактного класса
# animal = Animal() # TypeError: Can't instantiate abstract class
# Правильно: создать конкретный класс
class Dog(Animal):
def make_sound(self):
return "Woof!"
def move(self):
return "Running on four legs"
dog = Dog()
print(dog.make_sound()) # Woof!
print(dog.description()) # I am a Dog
print(dog.move()) # Running on four legs
Абстрактные методы
Абстрактный метод — это метод, который должен быть переопределён в подклассе:
from abc import ABC, abstractmethod
class Vehicle(ABC):
@abstractmethod
def start(self):
"""Запустить транспортное средство"""
pass
@abstractmethod
def stop(self):
"""Остановить транспортное средство"""
pass
class Car(Vehicle):
def start(self):
return "Car engine started"
def stop(self):
return "Car engine stopped"
class Bicycle(Vehicle):
def start(self):
return "Start pedaling"
def stop(self):
return "Stop pedaling"
car = Car()
print(car.start()) # Car engine started
print(car.stop()) # Car engine stopped
bike = Bicycle()
print(bike.start()) # Start pedaling
Абстрактные свойства (properties)
Можно сделать абстрактным и свойство:
from abc import ABC, abstractmethod
class Shape(ABC):
@property
@abstractmethod
def area(self):
pass
@property
@abstractmethod
def perimeter(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius ** 2
@property
def perimeter(self):
return 2 * 3.14159 * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
@property
def perimeter(self):
return 2 * (self.width + self.height)
circle = Circle(5)
print(f"Circle area: {circle.area:.2f}")
rect = Rectangle(4, 6)
print(f"Rectangle area: {rect.area}")
Класс с методом по умолчанию
Абстрактный метод может иметь реализацию по умолчанию:
from abc import ABC, abstractmethod
class Logger(ABC):
@abstractmethod
def log(self, message):
"""Логировать сообщение (по умолчанию добавляем префикс)"""
print(f"[LOG] {message}")
class FileLogger(Logger):
def log(self, message):
# Вызываем реализацию родителя
super().log(message)
# Добавляем свою логику
with open('log.txt', 'a') as f:
f.write(f"{message}\n")
class ConsoleLogger(Logger):
def log(self, message):
# Переопределяем полностью
print(f"[CONSOLE] {message}")
file_logger = FileLogger()
file_logger.log("Something happened") # Вызывает и родителя, и свою логику
Абстрактные классовые методы
Можно сделать абстрактным и classmethod:
from abc import ABC, abstractmethod
class DatabaseDriver(ABC):
@classmethod
@abstractmethod
def connect(cls, connection_string):
pass
@abstractmethod
def execute(self, query):
pass
class PostgresDriver(DatabaseDriver):
@classmethod
def connect(cls, connection_string):
return f"Connected to PostgreSQL: {connection_string}"
def execute(self, query):
return f"Executing: {query}"
class MySQLDriver(DatabaseDriver):
@classmethod
def connect(cls, connection_string):
return f"Connected to MySQL: {connection_string}"
def execute(self, query):
return f"MySQL query: {query}"
print(PostgresDriver.connect("localhost:5432"))
mysql = MySQLDriver()
print(mysql.execute("SELECT *"))
Проверка на абстрактность
Mожно проверить, является ли класс абстрактным:
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def method(self):
pass
class Concrete(Base):
def method(self):
return "implemented"
print(hasattr(Base, '__abstractmethods__')) # True
print(Base.__abstractmethods__) # frozenset({'method'})
print(hasattr(Concrete, '__abstractmethods__')) # False
Практический пример: ORM
from abc import ABC, abstractmethod
from typing import List, Generic, TypeVar
T = TypeVar('T')
class Repository(ABC, Generic[T]):
"""Интерфейс для хранилища данных"""
@abstractmethod
def find_by_id(self, id: int) -> T:
pass
@abstractmethod
def find_all(self) -> List[T]:
pass
@abstractmethod
def save(self, entity: T) -> None:
pass
@abstractmethod
def delete(self, id: int) -> None:
pass
class User:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
class UserRepository(Repository[User]):
def __init__(self):
self.users = {}
def find_by_id(self, id: int) -> User:
return self.users.get(id)
def find_all(self) -> List[User]:
return list(self.users.values())
def save(self, entity: User) -> None:
self.users[entity.id] = entity
def delete(self, id: int) -> None:
del self.users[id]
repo = UserRepository()
user = User(1, "Alice")
repo.save(user)
print(repo.find_by_id(1).name) # Alice
Старый способ (не используется)
До введения ABC в Python 2.6, использовался следующий подход:
class OldStyle:
def abstract_method(self):
raise NotImplementedError("Subclasses must implement this")
class Implementation(OldStyle):
def abstract_method(self):
return "Implemented"
Этот способ работает, но менее явный и не предотвращает создание экземпляра базового класса.
Различие между ABC и обычным классом
from abc import ABC, abstractmethod
# ABC подход (правильный)
class GoodBase(ABC):
@abstractmethod
def required_method(self):
pass
# Обычный класс с проверкой (плохой)
class BadBase:
def required_method(self):
raise NotImplementedError()
# GoodBase() → TypeError: Can't instantiate
# BadBase() → создаётся (ошибка будет позже)
bad = BadBase()
# Ошибка только при вызове метода
bad.required_method() # NotImplementedError
Заключение
Для создания абстрактного класса в Python:
- Импортируй
from abc import ABC, abstractmethod - Наследуй класс от
ABC - Помечай абстрактные методы декоратором
@abstractmethod - Реализуй все абстрактные методы в подклассах
- Используй для определения интерфейсов и контрактов
Абстрактные классы помогают:
- Определить контракт для подклассов
- Предотвратить создание неполных классов
- Сделать код более типизированным и понятным
- Реализовать паттерны как Strategy, Template Method и т.д.