Відповіді:
Що ви побачите іноді, це наступне:
class Abstract1( object ):
"""Some description that tells you it's abstract,
often listing the methods you're expected to supply."""
def aMethod( self ):
raise NotImplementedError( "Should have implemented this" )
Оскільки Python не має (і не потребує) формального контракту на інтерфейс, різниці між абстракцією та інтерфейсом у Java не існує. Якщо хтось докладе зусиль для визначення формального інтерфейсу, це також буде абстрактним класом. Єдині відмінності полягали б у заявленому намірі в docstring.
А різниця між абстрактним та інтерфейсом - це різання волосся, коли ви набираєте качку.
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
При цьому використовується своєрідний абстрактний надклас з міксинами для створення конкретних підкласів, які є непересічними.
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
є більш інформативним повідомлення про помилку :)
Яка різниця між абстрактним класом та інтерфейсом у 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
Наприклад, "effable" визначається як щось, що можна виразити словами. Скажіть, що ми хотіли б визначити абстрактний базовий клас, який можна додати, в 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.
У Python> = 2.6 є абстрактні базові класи .
Анотація базових класів (скорочено ABC) доповнює типізацію качок, надаючи спосіб визначення інтерфейсів, коли інші методи, такі як hasattr (), будуть незграбними. Python оснащений багатьма вбудованими АВС для структур даних (у модулі колекції), чисел (у модулі чисел) та потоків (у модулі io). Ви можете створити свій власний ABC за допомогою модуля abc.
Існує також модуль Zope Interface , який використовується проектами поза зоною, як виті. Я не дуже знайомий з ним, але є вікі сторінка тут , що може допомогти.
Взагалі вам не потрібна концепція абстрактних класів або інтерфейсів у python (відредаговано - детальну інформацію див. У відповіді С.Лотта).
У Python насправді немає жодної концепції.
Він використовує типи качок, які усунули потребу в інтерфейсах (принаймні для комп'ютера :-))
Python <= 2.5: Базові класи, очевидно, існують, але явного способу позначити метод як "чистий віртуальний" немає, тому клас насправді не є абстрактним.
Python> = 2.6: Абстрактні базові класи існують ( http://docs.python.org/library/abc.html ). І дозволяють вказати методи, які необхідно реалізувати в підкласах. Синтаксис мені не дуже подобається, але особливість є. У більшості випадків, мабуть, краще використовувати набирання качок з боку клієнта, що «використовує».
Більш простим способом пояснити: Інтерфейс схожий на порожню каструлю для булочок. Це файл класу з набором визначень методів, які не мають коду.
Абстрактний клас - це те саме, але не всі функції повинні бути порожніми. Деякі можуть мати код. Це не суворо порожньо.
Чому розрізняти: Існує не так багато практичної різниці в Python, але на рівні планування для великого проекту може бути звичайніше говорити про інтерфейси, оскільки немає коду. Особливо, якщо ви працюєте з програмістами Java, які звикли до цього терміну.
Взагалі інтерфейси використовуються лише мовами, що використовують модель класу з єдиним успадкуванням. У цих мовах єдиного успадкування зазвичай використовуються інтерфейси, якщо будь-який клас міг би використовувати певний метод або набір методів. Також у цих мовах одного успадкування абстрактні класи використовуються для того, щоб або мати визначені змінні класу на додаток до жодних або більше методів, або використовувати модель єдиного успадкування для обмеження діапазону класів, які могли використовувати набір методів.
Мови, що підтримують модель множинного успадкування, як правило, використовують лише класи або абстрактні базові класи, а не інтерфейси. Оскільки Python підтримує багатократне успадкування, він не використовує інтерфейси, і ви хочете використовувати базові класи або абстрактні базові класи.
Абстрактні заняття - це заняття, які містять один або кілька абстрактних методів. Поряд з абстрактними методами, Абстрактні класи можуть мати статичні, класичні та екземплярні методи. Але у випадку інтерфейсу він матиме лише абстрактні методи, не інші. Отже, не обов'язково успадковувати абстрактний клас, але воно є обов'язковим для успадкування інтерфейсу.
Для повноти слід згадати PEP3119, де ABC був введений та порівняний з інтерфейсами, та оригінальні талінські коментар .
Абстрактний клас не є ідеальним інтерфейсом:
Але якщо ви вирішили написати це по-своєму:
def some_function(self):
raise NotImplementedError()
interface = type(
'your_interface', (object,),
{'extra_func': some_function,
'__slots__': ['extra_func', ...]
...
'__instancecheck__': your_instance_checker,
'__subclasscheck__': your_subclass_checker
...
}
)
ok, rather as a class
or as a metaclass
and fighting with python to achieve the immutable object
and doing refactoring
...
ви досить швидко зрозумієте, що вигадуєте колесо, щоб врешті досягти
abc.ABCMeta
abc.ABCMeta
було запропоновано як корисне доповнення відсутньої функціональності інтерфейсу, і це досить справедливо на такій мові, як python.
Звичайно, це вдалося покращити краще під час написання версії 3 та додавання нового синтаксису та незмінної концепції інтерфейсу ...
Висновок:
The abc.ABCMeta IS "pythonic" interface in python