Ответ
Основной недостаток в том, что проверка на полную реализацию интерфейса происходит во время выполнения (runtime) при попытке создать экземпляр класса, а не на этапе статического анализа или определения класса.
Это означает, что ошибка из-за нереализованного абстрактного метода может проявиться только после запуска приложения, а не во время разработки или компиляции.
Пример:
from abc import ABC, abstractmethod
class StorageInterface(ABC):
@abstractmethod
def save(self, data: bytes):
pass
@abstractmethod
def load(self, key: str) -> bytes:
pass
# Класс определен без ошибок, хотя метод load() не реализован
class FileStorage(StorageInterface):
def save(self, data: bytes):
print("Data saved to file.")
# Ошибка возникнет только здесь, при попытке создания объекта
# TypeError: Can't instantiate abstract class FileStorage
# with abstract method load
try:
storage = FileStorage()
except TypeError as e:
print(e)
Ключевые недостатки:
- Позднее обнаружение ошибок: Ошибки выявляются только при инстанцировании, а не при определении класса.
- Отсутствие проверки сигнатур:
abcне проверяет соответствие типов и количества аргументов в реализованных методах. - Жесткая связь: Класс-реализация должен явно наследоваться от абстрактного класса, что создает сильную связь в иерархии классов.
Современная альтернатива:
Для определения интерфейсов в современном Python предпочтительнее использовать Протоколы (typing.Protocol), введенные в PEP 544. Они поддерживают статическую типизацию (утиная типизация) и позволяют статическим анализаторам (как Mypy) выявлять несоответствия интерфейсу еще до запуска кода.
Ответ 18+ 🔞
А, слушай, вот эта ваша абстрактная хуйня в питоне, она же, блядь, чисто на доверии работает! Как будто в рот меня чих-пых, честное пионерское!
Смотри, в чём прикол-то. Ты объявляешь интерфейс, там методы всякие, и думаешь: "Всё, щас компилятор за мной проследит, чтобы я ничего не забыл". А нихуя! Ошибка-то вылезет не когда ты пишешь класс, а только когда ты его, сука, создавать пытаешься! Во время работы программы, ёпта!
Вот смотри, пример, блядь:
from abc import ABC, abstractmethod
class StorageInterface(ABC):
@abstractmethod
def save(self, data: bytes):
pass
@abstractmethod
def load(self, key: str) -> bytes:
pass
# Смотри, я тут накосячил, метод load не написал. Но IDE молчит, как партизан!
class FileStorage(StorageInterface):
def save(self, data: bytes):
print("Data saved to file.")
# И вот тут всё ещё тишина, блядь! Всё компилируется!
# А пиздец наступит вот здесь:
try:
storage = FileStorage() # БАБАХ! TypeError вылезет только сейчас!
except TypeError as e:
print(e)
Вот тебе и "интерфейс", мать его! Ошибка как мина замедленного действия — сидит и ждёт, пока ты код запустишь. Представь, ты уже на прод выкатил, а тут — оп-па! — класс не инстанциируется. Красота, да?
Короче, главные косяки этой схемы:
- Ошибки ловятся поздно, как поезд. Не при написании, а при запуске. Удобно, блядь, особенно если код редко запускаемый.
- Сигнатуры не проверяет. Можешь метод назвать правильно, но аргументы там наворотить какие угодно —
abcпромолчит, как будто так и надо. Пиздец, а не контроль. - Жёстко привязывает по родству. Наследник должен быть прям сыном абстрактного класса, иначе нихуя не работает. Архитектура, блядь, деревянная получается.
Так что делать-то, спросишь? А есть же, блядь, современная штука — Протоколы (typing.Protocol). Вот это уже ближе к правде жизни. Они со статическими анализаторами (типа Mypy) дружат. Ты код пишешь, а тебе сразу подсказывают: "Э, дружок-пирожок, а метод-то load ты забыл, пидарас шерстяной!". Вот это уже дело. Утиная типизация в её лучшем, ебать, проявлении.