Python Language
Abstract Base Classes (abc)
Ricerca…
Impostazione del metaclass ABCMeta
Le classi astratte sono classi che devono essere ereditate ma evitare l'implementazione di metodi specifici, lasciando solo le firme dei metodi che le sottoclassi devono implementare.
Le classi astratte sono utili per definire e applicare le astrazioni di classe ad un livello elevato, simile al concetto di interfacce nei linguaggi tipizzati, senza la necessità di implementazione del metodo.
Un approccio concettuale alla definizione di una classe astratta consiste nello stub dei metodi di classe, quindi genera un NotImplementedError se vi si accede. Questo impedisce alle classi di bambini di accedere ai metodi padre senza sovrascriverli prima. Così:
class Fruit:
def check_ripeness(self):
raise NotImplementedError("check_ripeness method not implemented!")
class Apple(Fruit):
pass
a = Apple()
a.check_ripeness() # raises NotImplementedError
La creazione di una classe astratta in questo modo impedisce l'uso improprio di metodi che non sono sovrascritti, e certamente incoraggia i metodi da definire nelle classi figlie, ma non impone la loro definizione. Con il modulo abc
possiamo impedire l'istanziazione delle classi figlie quando non riescono a scavalcare i metodi astratti di classe dei loro genitori e antenati:
from abc import ABCMeta
class AbstractClass(object):
# the metaclass attribute must always be set as a class variable
__metaclass__ = ABCMeta
# the abstractmethod decorator registers this method as undefined
@abstractmethod
def virtual_method_subclasses_must_define(self):
# Can be left completely blank, or a base implementation can be provided
# Note that ordinarily a blank interpretation implicitly returns `None`,
# but by registering, this behaviour is no longer enforced.
Ora è possibile semplicemente eseguire la sottoclasse e l'override:
class Subclass(AbstractClass):
def virtual_method_subclasses_must_define(self):
return
Perché / Come usare ABCMeta e @abstractmethod
Le astratte classi base (ABC) impongono le classi derivate che implementano particolari metodi dalla classe base.
Per capire come funziona e perché dovremmo usarlo, diamo un'occhiata a un esempio che Van Rossum avrebbe apprezzato. Diciamo che abbiamo una classe Base "MontyPython" con due metodi (joke & punchline) che devono essere implementati da tutte le classi derivate.
class MontyPython:
def joke(self):
raise NotImplementedError()
def punchline(self):
raise NotImplementedError()
class ArgumentClinic(MontyPython):
def joke(self):
return "Hahahahahah"
Quando istanziamo un oggetto e chiamiamo i suoi due metodi, avremo un errore (come previsto) con il metodo punchline()
.
>>> sketch = ArgumentClinic()
>>> sketch.punchline()
NotImplementedError
Tuttavia, questo ci consente comunque di creare un'istanza di un oggetto della classe ArgumentClinic senza ottenere un errore. In realtà non otteniamo un errore finché non cerchiamo la battuta ().
Ciò viene evitato usando il modulo Abstract Base Class (ABC). Vediamo come funziona con lo stesso esempio:
from abc import ABCMeta, abstractmethod
class MontyPython(metaclass=ABCMeta):
@abstractmethod
def joke(self):
pass
@abstractmethod
def punchline(self):
pass
class ArgumentClinic(MontyPython):
def joke(self):
return "Hahahahahah"
Questa volta, quando proviamo a istanziare un oggetto dalla classe incompleta, otteniamo immediatamente un errore TypeError!
>>> c = ArgumentClinic()
TypeError:
"Can't instantiate abstract class ArgumentClinic with abstract methods punchline"
In questo caso, è facile completare la classe per evitare qualsiasi TypeErrors:
class ArgumentClinic(MontyPython):
def joke(self):
return "Hahahahahah"
def punchline(self):
return "Send in the constable!"
Questa volta quando istanzia un oggetto funziona!