Разница между абстрактным классом и интерфейсом в Python

В чем разница между абстрактным классом и интерфейсом в Python?

Ответы

Ответ 1

Иногда вы увидите следующее:

class Abstract1( object ):
    """Some description that tells you it abstract,
    often listing the methods you're expected to supply."""
    def aMethod( self ):
        raise NotImplementedError( "Should have implemented this" )

Поскольку Python не имеет (и не нуждается) формального контракта Interface, различия в стиле Java между абстракцией и интерфейсом не существуют. Если кто-то попытается определить формальный интерфейс, он также будет абстрактным классом. Единственные различия были бы в заявленном намерении в докштрине.

И разница между абстракцией и интерфейсом - это прическа, когда вы печатаете утку.

Java использует интерфейсы, потому что у него нет множественного наследования.

Поскольку Python имеет множественное наследование, вы также можете увидеть что-то вроде этого

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

Здесь используется своего рода абстрактный суперкласс с mixins для создания конкретных подклассов, которые не пересекаются.

Ответ 2

В чем разница между абстрактным классом и интерфейсом в 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.

Ответ 3

Python >= 2.6 имеет Абстрактные базовые классы.

Абстрактные базовые классы (сокращенно ABC) дополняют утиную обеспечивая способ определения интерфейсов когда другие методы, такие как hasattr() будет неуклюжим. Python поставляется с многие встроенные ABC для структур данных (в модуле коллекций), числа (в модуле чисел) и потоки (в модуле io). Вы можете создать ваш собственный ABC с модулем abc.

Существует также модуль Zope Interface, который используется проектами вне zope, например, скрученными. Я действительно не знаком с этим, но здесь есть страница wiki , которая может помочь.

В общем, вам не нужно понятие абстрактных классов или интерфейсов в python (отредактировано - см. ответ S.Lott для деталей).

Ответ 4

У Python действительно нет понятия.

Он использует утиную печать, которая устраняет необходимость в интерфейсах (по крайней мере для компьютера: -))

Python <= 2,5: Базовые классы, очевидно, существуют, но нет явного способа пометить метод как "чистый виртуальный", поэтому класс не является абстрактным.

Python >= 2.6: Абстрактные базовые классы существуют (http://docs.python.org/library/abc.html). И вы можете указать методы, которые должны быть реализованы в подклассах. Мне не очень нравится синтаксис, но функция есть. В большинстве случаев, вероятно, лучше использовать утиную печать с "клиентской стороны".

Ответ 5

Более простой способ объяснить: Интерфейс подобен пустой кастрюле для кексов. Это файл класса с набором определений методов, которые не имеют кода.

Абстрактный класс - это одно и то же, но не все функции должны быть пустыми. У некоторых может быть код. Это не строго пусто.

Зачем дифференцировать: Там не так много практических различий в Python, но на уровне планирования для большого проекта было бы более распространено говорить об интерфейсах, так как нет никакого кода. Особенно, если вы работаете с Java-программистами, которые привыкли к этому термину.

Ответ 6

В общем, интерфейсы используются только на языках, использующих модель класса с одним наследованием. В этих языках с одним наследованием интерфейсы обычно используются, если какой-либо класс может использовать конкретный метод или набор методов. Также в этих языках с одним наследованием абстрактные классы используются либо для определения переменных класса в дополнение к ни одному или более методам, либо для использования модели с одним наследованием для ограничения диапазона классов, которые могут использовать набор методов.

Языки, поддерживающие модель множественного наследования, обычно используют только классы или абстрактные базовые классы, а не интерфейсы. Поскольку Python поддерживает множественное наследование, он не использует интерфейсы, и вы хотели бы использовать базовые классы или абстрактные базовые классы.

http://docs.python.org/library/abc.html

Ответ 7

Абстрактные классы - это классы, которые содержат один или несколько абстрактных методов. Наряду с абстрактными методами, абстрактные классы могут иметь статические методы, методы класса и экземпляра. Но в случае интерфейса у него будут только абстрактные методы, а не другие. Следовательно, не обязательно наследовать абстрактный класс, но обязательно наследовать интерфейс.