Odpowiedzi:
To, co czasem zobaczysz, jest następujące:
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" )
Ponieważ Python nie ma (i nie potrzebuje) formalnej umowy interfejsu, nie ma w tym języku rozróżnienia między abstrakcją a interfejsem. Jeśli ktoś przejdzie wysiłek zdefiniowania formalnego interfejsu, będzie to również klasa abstrakcyjna. Jedynymi różnicami byłyby określone zamiary w dokumentacji.
Różnica między abstrakcją a interfejsem polega na rozczesywaniu włosów, gdy piszesz kaczkę.
Java używa interfejsów, ponieważ nie ma wielokrotnego dziedziczenia.
Ponieważ Python ma wiele elementów dziedziczenia, możesz również zobaczyć coś takiego
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
Wykorzystuje to rodzaj abstrakcyjnej nadklasy z mixinami, aby utworzyć konkretne podklasy, które są rozłączne.
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
jest bardziej informacyjny komunikat o błędzie :)
Jaka jest różnica między klasą abstrakcyjną a interfejsem w Pythonie?
Interfejs dla obiektu to zestaw metod i atrybutów tego obiektu.
W Pythonie możemy użyć abstrakcyjnej klasy bazowej do zdefiniowania i wymuszenia interfejsu.
Powiedzmy, że chcemy użyć jednej z abstrakcyjnych klas podstawowych z collections
modułu:
import collections
class MySet(collections.Set):
pass
Jeśli spróbujemy go użyć, otrzymamy, TypeError
ponieważ utworzona przez nas klasa nie obsługuje oczekiwanego zachowania zestawów:
>>> 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__
Więc jesteśmy zobowiązani do wdrożenia w najmniej __contains__
, __iter__
i __len__
. Skorzystajmy z tego przykładu implementacji z dokumentacji :
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
Możemy stworzyć własną Abstrakcyjną Klasę Podstawową, ustawiając metaklasę abc.ABCMeta
i używając abc.abstractmethod
dekoratora na odpowiednich metodach. Metaklasa zostanie dodana udekorowane funkcje do __abstractmethods__
atrybutu, zapobiegając tworzeniu wystąpień, dopóki nie zostaną zdefiniowane.
import abc
Na przykład „efektywny” jest definiowany jako coś, co można wyrazić słowami. Powiedzmy, że chcieliśmy zdefiniować abstrakcyjną klasę bazową, która jest efektywna, w Pythonie 2:
class Effable(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Lub w Pythonie 3, z niewielką zmianą deklaracji metaklasy:
class Effable(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Teraz, jeśli spróbujemy stworzyć efektowny obiekt bez implementacji interfejsu:
class MyEffable(Effable):
pass
i spróbuj utworzyć go:
>>> MyEffable()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
Mówi się nam, że nie zakończyliśmy pracy.
Teraz, jeśli zastosujemy się do tego, zapewniając oczekiwany interfejs:
class MyEffable(Effable):
def __str__(self):
return 'expressable!'
jesteśmy w stanie użyć konkretnej wersji klasy pochodzącej z abstrakcyjnej:
>>> me = MyEffable()
>>> print(me)
expressable!
Są inne rzeczy, które moglibyśmy z tym zrobić, np. Zarejestrować wirtualne podklasy, które już implementują te interfejsy, ale myślę, że to wykracza poza zakres tego pytania. Inne metody przedstawione tutaj musiałyby dostosować tę metodę za pomocąabc
modułu.
Wykazaliśmy, że utworzenie abstrakcyjnej klasy bazowej definiuje interfejsy dla obiektów niestandardowych w języku Python.
Python> = 2.6 ma abstrakcyjne klasy podstawowe .
Abstrakcyjne klasy podstawowe (w skrócie ABC) uzupełniają pisanie kaczek, zapewniając sposób definiowania interfejsów, gdy inne techniki, takie jak hasattr (), byłyby niezdarne. Python zawiera wiele wbudowanych ABC dla struktur danych (w module kolekcji), liczb (w module liczb) i strumieni (w module io). Możesz stworzyć własne ABC za pomocą modułu abc.
Istnieje również moduł interfejsu Zope , który jest używany w projektach poza Zope , takich jak skręcone. Nie jestem do końca zaznajomiony z tym, ale tutaj jest strona wiki , która może pomóc.
Ogólnie rzecz biorąc, nie potrzebujesz koncepcji klas abstrakcyjnych ani interfejsów w Pythonie (edytowane - patrz odpowiedź S.Lott, aby uzyskać szczegółowe informacje).
Python tak naprawdę nie ma żadnej koncepcji.
Używa pisania kaczego, co eliminuje potrzebę interfejsów (przynajmniej dla komputera :-))
Python <= 2.5: Klasy podstawowe oczywiście istnieją, ale nie ma wyraźnego sposobu oznaczenia metody jako „czystej wirtualnej”, więc klasa nie jest tak naprawdę abstrakcyjna.
Python> = 2.6: Istnieją abstrakcyjne klasy podstawowe ( http://docs.python.org/library/abc.html ). I pozwalają określić metody, które muszą być zaimplementowane w podklasach. Nie podoba mi się składnia, ale funkcja jest dostępna. W większości przypadków prawdopodobnie lepiej jest pisać na kaczych stronach po stronie klienta.
W bardziej prosty sposób wyjaśnić: Interfejs przypomina rodzaj pustej mufinki. Jest to plik klasy z zestawem definicji metod, które nie mają kodu.
Klasa abstrakcyjna to to samo, ale nie wszystkie funkcje muszą być puste. Niektóre mogą mieć kod. Nie jest ściśle pusty.
Po co różnicować: Nie ma praktycznej różnicy w Pythonie, ale na poziomie planowania dużego projektu może być bardziej powszechne mówienie o interfejsach, ponieważ nie ma kodu. Zwłaszcza jeśli pracujesz z programistami Java, którzy są przyzwyczajeni do tego terminu.
Zasadniczo interfejsy są używane tylko w językach, które używają modelu klasy z pojedynczym dziedziczeniem. W tych językach z pojedynczym dziedzictwem interfejsy są zwykle używane, jeśli jakakolwiek klasa może użyć określonej metody lub zestawu metod. Również w tych językach z dziedziczeniem pojedynczym klasy abstrakcyjne mają albo zdefiniowane zmienne klas oprócz żadnej lub więcej metod, albo wykorzystują model pojedynczego dziedziczenia do ograniczenia zakresu klas, które mogłyby korzystać z zestawu metod.
Języki obsługujące model wielokrotnego dziedziczenia zwykle używają tylko klas lub abstrakcyjnych klas bazowych, a nie interfejsów. Ponieważ Python obsługuje wielokrotne dziedziczenie, nie używa interfejsów i chciałbyś użyć klas podstawowych lub abstrakcyjnych klas podstawowych.
Klasy abstrakcyjne to klasy zawierające jedną lub więcej metod abstrakcyjnych. Oprócz metod abstrakcyjnych klasy abstrakcyjne mogą mieć metody statyczne, klasy i instancji. Ale w przypadku interfejsu będzie miał tylko abstrakcyjne metody, a nie inne. Dlatego dziedziczenie klasy abstrakcyjnej nie jest obowiązkowe, ale obowiązkowe jest dziedziczenie interfejsu.
Dla kompletności powinniśmy wspomnieć o PEP3119, gdzie ABC zostało wprowadzone i porównane z interfejsami oraz oryginalny komentarz Talina .
Klasa abstrakcyjna nie jest doskonałym interfejsem:
Ale jeśli pomyślisz o napisaniu go po swojemu:
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
...
dość szybko zdasz sobie sprawę, że wymyślasz koło, by ostatecznie osiągnąć
abc.ABCMeta
abc.ABCMeta
został zaproponowany jako użyteczny dodatek do brakującej funkcjonalności interfejsu, co jest dość sprawiedliwe w języku takim jak python.
Z pewnością można go było ulepszyć podczas pisania wersji 3 oraz dodając nową składnię i niezmienną koncepcję interfejsu ...
Wniosek:
The abc.ABCMeta IS "pythonic" interface in python