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!



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow