04: I Metodi di Classe - Programmare in Python

Video Corso Programmazione a Oggetti Python 3

04: I Metodi di Classe

Ragazzi e Ragazze bentornati!

Introduciamo oggi il concetto di Metodo di Classe nella Programmazione a Oggetti in Python.

Questa nuova tiplogia di metodo va ad affiancarsi ai metodi "classici" che abbiamo visto fin'ora. Come ricorderete, questi ultimi sono delle funzioni interne alle nostre classi, a cui viene sempre passato come primo parametro self, che ricordiamolo brevemente, rappresenta una referenza a ciascuna Istanza della Classe, e che ci permette quindi di utilizzare il Metodo facilmente, su ciascun oggetto creato.

class Persona:

    def __init__(self, nome, cognome, età, residenza):
        self.nome = nome
        self.cognome = cognome
        self.età = età
        self.residenza = residenza

    def scheda_personale(self):
        scheda = f"""
        Nome: {self.nome} 
        Cognome: {self.cognome}
        Età: {self.età}
        Residenza: {self.residenza}\n"""
        return scheda
        
    def modifica_scheda(self):
        print("""Modifica Scheda:
        1 - Nome
        2 - Cognome
        3 - Età
        4 - Residenza""")

        scelta = input("Cosa Desideri Modificare?")
        if scelta == "1":
            self.nome = input("Nuovo Nome --> ")
        if scelta == "2":
            self.cognome = input("Nuovo cognome --> ")
        if scelta == "3":
            self.età = input("Nuovo età --> ")
        if scelta == "4":
            self.residenza = input("Nuovo residenza --> ")
            
            
class Studente(Persona):
    profilo = "Studente"

    def __init__(self, nome, cognome, età, residenza, corso_di_studio):
        super().__init__(nome, cognome, età, residenza)
        self.corso_di_studio = corso_di_studio

    def scheda_personale(self):
        scheda = f"""
        Profilo:{Studente.profilo}
        Corso Di Studi:{self.corso_di_studio}
        ***"""
        return super().scheda_personale() + scheda

    def cambio_corso(self, corso):
        self.corso_di_studio = corso
        print("Corso Aggiornato")
        
        
class Insegnante(Persona):
    profilo = "Insegnante"

    def __init__(self, nome, cognome, età, residenza, materie=None):
        super().__init__(nome, cognome, età, residenza)
        if materie is None:
            self.materie = []
        else:
            self.materie = materie

    def scheda_personale(self):
        scheda = f"""
        Profilo:{Insegnante.profilo}
        Materie Insegnate:{self.materie}
        ***"""
        return super().scheda_personale() + scheda

    def aggiungi_materia(self, nuova):
        if nuova not in self.materie:
            self.materie.append(nuova)
        print("Elenco Materie Aggiornato")

Bene, iniziamo ora a parlare del nuovo argomento.

Tra gli usi dei Metodi di Classe, quello più comune è di Costruttore Alternativo, ovvero un metodo parallelo ad __init__ per instanziare oggetti, che possiamo utilizzare in alcuni contesti particolari.

Supponiamo che all'operatore incaricato di creare le varie istanze di studente e insegnante nel nostro programma, venga passata una lista di persone su file testuale, in cui nome, cognome, età e residenza siano separate da un punto e virgola, o da un trattino, in questo modo:

iron_man = "Tony-Stark-40-Torre Stark"

In questo caso l'operatore dovrebbe quindi andare a copiarsi dal file testuale i vari attributi uno ad uno, stando attento a non copiare per sbaglio anche il trattino, e quindi passarli ad __init__ ... un lavoraccio!

Per facilitare questo lavoro, il nostro costruttore alternativo sarà in grado di gestire questa casistica, prelevando i dati automaticamente da ciascuna stringa passata.

Il primo parametro ad essergli passato tra parentesi non è più l'Istanza, ma la Classe stessa, quindi, tenete a mente, i Metodi di Classe sono legati alla Classe, e non invece, alle Istanze.

Convenzionalmente parlando, come parametro non usermo quindi più self ma cls. I più attenti tra voi staranno ora pensando: Ma Mike, se si tratta solo di convenzioni, anche se cambi il nome al parametro, quella che viene passata resta sempre l'Istanza, no? Be avete ragione.

Infatti, oltre a cambiare il nome del parametro, per fare in modo che Python passi effettivamente la Classe invece che l'Istanza, utilizziamo un decoratore, @classmethod cioé un comando specifico per lo scopo, in questo modo:

class Persona:

    def __init__(self, nome, cognome, età, residenza):
        self.nome = nome
        self.cognome = cognome
        self.età = età
        self.residenza = residenza

    @classmethod
    def costruttore_alternativo(cls):
        pass

    # (...) resto del codice

State sereni: parleremo di decoratori nel dettaglio in un altro video, per ora basta sapere che questo fantastico strumento ci permette di alterare il comportamento dei Metodi a nostro piacimento, e che @classmethod è il decoratore che si utilizza per creare i Metodi di Classe, e che permette quindi, ripetiamolo, di passare come parametro la Classe invece dell'Istanza.

La convenzione in questi casi vuole inoltre che il nome sia abbastanza specifico sullo scopo del costruttore alternativo, lo chiameremo quindi come da prassi in questi casi, from_string. Tutto chiaro? Proseguiamo quindi con la scrittura del codice.

class Persona:

    def __init__(self, nome, cognome, età, residenza):
        self.nome = nome
        self.cognome = cognome
        self.età = età
        self.residenza = residenza

    @classmethod
    def from_string(cls, stringa_persona):
        nome, cognome, età, residenza = stringa_persona.split("-")
        return cls(nome, cognome, età, residenza)

Possiamo ora passare la stringa iron_man al nostro costruttore alternativo from_string, e creare un'Istanza di Persona.

persona1 = Persona.from_string(iron_man)
print(persona1.scheda_personale())

  Nome: Tony
  Cognome: Stark
  Età: 40
  Residenza: Torre Stark

Come vedete, tutto funziona a meraviglia! Abbiamo sicuramente semplificato notevolmente il lavoro del nostro caro operatore o operatrice, riuscendo ad istanziare la persona di Tony Stark.

Visto che anche questo metodo viene chiaramente ereditato automaticamente dalle sottoclassi di Persona, possiamo magari istanziare Iron Man come Insegnante di Ingegneria, che ve ne pare?

insg1 = Insegnante.from_string(iron_man, "Ingegneria")

TypeError: from_string() takes 2 positional arguments but 3 were given

Sorpresa! Otteniamo un bell'errore!

Ma niente panico, analizziamolo: ci viene detto che abbiamo passato un parametro di troppo! Ma come sarebbe? Ma insegnante ha anche le Materie no? La verità è che si, insegnante ha le sue materie, ma queste sono definite nel SUO metodo costruttore __init__, e ci stava inoltre super() a gestire tutto il resto degli attributi, ricordate?

def __init__(self, nome, cognome, età, residenza, materie=None): super().__init__(nome, cognome, età, residenza)

Visto che quindi, come abbiamo visto, a seconda del caso c'è la possibilità di avere dei parametri in più, talvolta in meno, utilizzeremo ora una piccola magia di Python:

@classmethod def from_string(cls, stringa_ persona, *args): nome, cognome, età, residenza = stringa_persona.split("-") return cls(nome, cognome, età, residenza, *args)

*args è la notazione specifica in Python per casi come questi, in cui possono esserci zero oppure vari parametri aggiuntivi da passare alle nostre funzioni, e come vedete ora è utilissima in contesti in cui si usa l'Ereditarietà, permettendo ai nostri Metodi di restare quanto più generici possibile.

Ci siamo? Piuttosto semplice in verità non trovate? Allora proseguiamo!

iron_man = "Tony-Stark-40-Torre Stark"
zuck = "Mark-Zuckerberg-33-California"

insg1 = Insegnante.from_string(iron_man,"Ingegneria")
stud1 = Studente.from_string(zuck,"SEO")

print(insg1.scheda_personale())
print(stud1.scheda_personale())


  Nome: Tony
  Cognome: Stark
  Età: 40
  Residenza: Torre Stark

  Profilo:Insegnante
  Materie Insegnate:Ingegneria
  ***

  Nome: Mark
  Cognome: Zuckerberg
  Età: 33
  Residenza: California

  Profilo:Studente
  Corso Di Studi:SEO
  ***

Un altro caso d'uso in cui i Metodi di Classe si dimostrano utili, è qualora vogliate un Metodo che cambia comporamento in base alla sottoclasse che lo sta richiamando. Ad esempio, possiamo creare un Metodo chiamato occupazione, che ci permette di identificare uno studente o un insegnante senza dover passare per la scheda personale.

class Persona:

    def __init__(self, nome, cognome, età, residenza):
        self.nome = nome
        self.cognome = cognome
        self.età = età
        self.residenza = residenza

    @classmethod
    def from_string(cls, stringa_persona, *args):
        nome, cognome, età, residenza = stringa_persona.split("-")
        return cls(nome, cognome, età, residenza, *args)

    # Esempio didattico: per ottenere lo stesso effetto con meno codice avremo potuto fare direttamente return cls.__nome__     
    @classmethod			    
    def occupazione(cls):
        if (cls.__name__ == "Studente"):
            return "Studente"
        else:
            return "Insegnante"
print(insg1.occupazione())
print(stud1.occupazione())

Insegnante
Studente

Un terzo caso d'uso potrebbe essere l'utilizzo di un Metodo di Classe per modificare qualche proprietà della Classe Genitore, ad esempio potreste avere una Variabile di Classe chiamata istituto inizializzata con la stringa "Scuola Superiore", e creare un Metodo di Classe che vi consente quindi di modificarne il valore, assegnando ad esempio la stringa "Università".

Io vi ho dato l'idea, provate ora voi ad esercitarvi e a testare i limiti di questa tecnica!




Menu della Serie