Risposte:
Quello che vedrai a volte è il seguente:
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" )
Poiché Python non ha (e non ha bisogno) un contratto formale di Interfaccia, la distinzione in stile Java tra astrazione e interfaccia non esiste. Se qualcuno passa attraverso lo sforzo di definire un'interfaccia formale, sarà anche una classe astratta. Le uniche differenze sarebbero nell'intenzione dichiarata nella dottring.
E la differenza tra astratto e interfaccia è una cosa spaccante quando si digita l'anatra.
Java utilizza le interfacce perché non ha ereditarietà multiple.
Poiché Python ha un'eredità multipla, potresti anche vedere qualcosa del genere
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
Questo utilizza una sorta di superclasse astratta con mixin per creare sottoclassi concrete che sono disgiunte.
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
è un messaggio di errore più informativo :)
Qual è la differenza tra la classe astratta e l'interfaccia in Python?
Un'interfaccia, per un oggetto, è un insieme di metodi e attributi su quell'oggetto.
In Python, possiamo usare una classe base astratta per definire e applicare un'interfaccia.
Ad esempio, supponiamo di voler utilizzare una delle classi di base astratte dal collections
modulo:
import collections
class MySet(collections.Set):
pass
Se proviamo a usarlo, otteniamo un TypeError
perché la classe che abbiamo creato non supporta il comportamento previsto degli insiemi:
>>> 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__
Così ci viene richiesto di attuare, a almeno __contains__
, __iter__
e __len__
. Usiamo questo esempio di implementazione dalla documentazione :
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
Possiamo creare la nostra classe base astratta impostando la metaclasse abc.ABCMeta
e usando il abc.abstractmethod
decoratore su metodi pertinenti. Alla metaclasse verranno aggiunte le funzioni decorate __abstractmethods__
all'attributo, impedendo l'istanza fino a quando non vengono definite.
import abc
Ad esempio, "effable" è definito come qualcosa che può essere espresso in parole. Supponiamo di voler definire una classe di base astratta che sia efficace, in Python 2:
class Effable(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
O in Python 3, con il leggero cambiamento nella dichiarazione della metaclasse:
class Effable(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Ora se proviamo a creare un oggetto effable senza implementare l'interfaccia:
class MyEffable(Effable):
pass
e tenta di istanziarlo:
>>> MyEffable()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
Ci viene detto che non abbiamo finito il lavoro.
Ora, se rispettiamo fornendo l'interfaccia prevista:
class MyEffable(Effable):
def __str__(self):
return 'expressable!'
siamo quindi in grado di utilizzare la versione concreta della classe derivata da quella astratta:
>>> me = MyEffable()
>>> print(me)
expressable!
Ci sono altre cose che possiamo fare con questo, come la registrazione di sottoclassi virtuali che già implementano queste interfacce, ma penso che esuli dallo scopo di questa domanda. Gli altri metodi qui dimostrati dovrebbero adattare questo metodo usando il abc
modulo per farlo, tuttavia.
Abbiamo dimostrato che la creazione di una classe base astratta definisce le interfacce per oggetti personalizzati in Python.
Python> = 2.6 ha Classi di base astratte .
Le classi di base astratte (ABC abbreviate) completano la tipizzazione anatra fornendo un modo per definire le interfacce quando altre tecniche come hasattr () sarebbero goffe. Python viene fornito con molti ABC integrati per strutture dati (nel modulo collezioni), numeri (nel modulo numeri) e flussi (nel modulo io). Puoi creare la tua ABC con il modulo abc.
C'è anche il modulo Zope Interface , che viene utilizzato da progetti al di fuori di zope, come twisted. Io non sono davvero familiarità con essa, ma c'è una pagina wiki qui che aiuto potrebbe.
In generale, non è necessario il concetto di classi astratte o interfacce in Python (modificato - vedere la risposta di S.Lott per i dettagli).
Python non ha davvero alcun concetto.
Utilizza la tipizzazione duck, che ha rimosso la necessità di interfacce (almeno per il computer :-))
Python <= 2.5: esistono ovviamente classi di base, ma non esiste un modo esplicito di contrassegnare un metodo come "puro virtuale", quindi la classe non è veramente astratta.
Python> = 2.6: esistono classi base astratte ( http://docs.python.org/library/abc.html ). E ti consente di specificare i metodi che devono essere implementati nelle sottoclassi. Non mi piace molto la sintassi, ma la funzione è lì. Il più delle volte è probabilmente meglio usare la tipizzazione duck dal lato client "using".
In un modo più semplice per spiegare: un'interfaccia è un po 'come una teglia per muffin vuota. È un file di classe con un insieme di definizioni di metodi che non hanno codice.
Una classe astratta è la stessa cosa, ma non tutte le funzioni devono essere vuote. Alcuni possono avere il codice. Non è rigorosamente vuoto.
Perché differenziare: non c'è molta differenza pratica in Python, ma a livello di pianificazione per un grande progetto, potrebbe essere più comune parlare di interfacce, poiché non c'è codice. Soprattutto se stai lavorando con programmatori Java che sono abituati al termine.
In generale, le interfacce vengono utilizzate solo nei linguaggi che utilizzano il modello di classe a ereditarietà singola. In questi linguaggi a eredità singola, le interfacce vengono in genere utilizzate se una classe può utilizzare un metodo o un insieme di metodi particolari. Anche in questi linguaggi a eredità singola, le classi astratte vengono utilizzate per avere variabili di classe definite oltre a nessuno o più metodi o per sfruttare il modello a ereditarietà singola per limitare l'intervallo di classi che potrebbero utilizzare un insieme di metodi.
I linguaggi che supportano il modello di ereditarietà multipla tendono a utilizzare solo classi o classi di base astratte e non interfacce. Poiché Python supporta l'ereditarietà multipla, non utilizza interfacce e si desidera utilizzare le classi di base o le classi di base astratte.
Le classi astratte sono classi che contengono uno o più metodi astratti. Insieme ai metodi astratti, le classi astratte possono avere metodi statici, di classe e di istanza. Ma in caso di interfaccia, avrà solo metodi astratti, non altri. Quindi non è obbligatorio ereditare la classe astratta ma è obbligatorio ereditare l'interfaccia.
Per completezza, dovremmo menzionare PEP3119 in cui la ABC è stata introdotta e confrontata con le interfacce e il commento originale di Talin .
La classe astratta non è un'interfaccia perfetta:
Ma se pensi di scriverlo a modo tuo:
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
...
ti accorgerai abbastanza rapidamente che stai inventando la ruota per raggiungere alla fine
abc.ABCMeta
abc.ABCMeta
è stato proposto come utile aggiunta della funzionalità dell'interfaccia mancante, ed è abbastanza giusto in un linguaggio come Python.
Certamente, è stato in grado di essere migliorato meglio durante la scrittura della versione 3 e l'aggiunta di una nuova sintassi e il concetto di interfaccia immutabile ...
Conclusione:
The abc.ABCMeta IS "pythonic" interface in python