В чем разница между абстрактным классом и интерфейсом в Python?
Интерфейс для объекта - это набор методов и атрибутов для этого объекта.
В Python мы можем использовать абстрактный базовый класс для определения и реализации интерфейса.
Использование абстрактного базового класса
Например, скажем, мы хотим использовать один из абстрактных базовых классов из модуля collections
:
import collections
class MySet(collections.Set):
pass
Если мы попытаемся использовать его, мы получим TypeError
, потому что созданный нами класс не поддерживает ожидаемое поведение наборов:
>>> MySet()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MySet with abstract methods
__contains__, __iter__, __len__
Таким образом, мы должны реализовать на минимум __contains__
, __iter__
и __len__
. Давайте использовать этот пример реализации из документации :
class ListBasedSet(collections.Set):
"""Alternate set implementation favoring space over speed
and not requiring the set elements to be hashable.
"""
def __init__(self, iterable):
self.elements = lst = []
for value in iterable:
if value not in lst:
lst.append(value)
def __iter__(self):
return iter(self.elements)
def __contains__(self, value):
return value in self.elements
def __len__(self):
return len(self.elements)
s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2
Реализация: создание абстрактного базового класса
Мы можем создать наш собственный абстрактный базовый класс, установив метакласс на abc.ABCMeta
и используя декоратор abc.abstractmethod
для соответствующих методов. В метакласс добавят декорированные функции к атрибуту __abstractmethods__
, предотвращая создание экземпляров до тех пор, пока они не будут определены.
import abc
Например, «выполнимый» определяется как нечто, что можно выразить словами. Скажем, мы хотели определить абстрактный базовый класс, который является эффективным, в Python 2:
class Effable(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Или в Python 3 с небольшим изменением в объявлении метакласса:
class Effable(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Теперь, если мы попытаемся создать эффективный объект без реализации интерфейса:
class MyEffable(Effable):
pass
и попытаться создать его экземпляр:
>>> MyEffable()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
Нам говорят, что мы не завершили работу.
Теперь, если мы обеспечим соответствие ожидаемому интерфейсу:
class MyEffable(Effable):
def __str__(self):
return 'expressable!'
тогда мы можем использовать конкретную версию класса, полученную из абстрактного:
>>> me = MyEffable()
>>> print(me)
expressable!
Есть другие вещи, которые мы могли бы сделать с этим, например, зарегистрировать виртуальные подклассы, которые уже реализуют эти интерфейсы, но я думаю, что это выходит за рамки этого вопроса. Однако другие методы, продемонстрированные здесь, должны будут адаптировать этот метод с использованием модуля abc
.
Заключение
Мы продемонстрировали, что создание абстрактного базового класса определяет интерфейсы для пользовательских объектов в Python.