Skip to content

12. Classi e oggetti

Una classe è il modello da cui vengono creati gli oggetti. Si dice che un oggetto sia un'istanza di una classe.

Image

Nota: la cartella [shared] è stata inserita nella directory [Root Sources] del progetto.

12.1. script [classes_01]: una classe Object

Lo script [classes_01] mostra un uso obsoleto delle classi:

#  a class
class Objet(object):
    """une classe Objet vide"""


#  any variable of type [Object] can have attributes by construction
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
#  displays the
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
#  modify object
obj1.attr2 += 100
#  displays the
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
#  assign reference obj1 to obj2
obj2 = obj1
#  modifies the object pointed to by obj2
obj2.attr2 = 0
#  displays both objects - obj1 now points to a modified object
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
#  obj1 and obj2 point to the same object
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
#  instance type obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
#  any type is an object in Python
print(f"type(4)={type(4)}")
print(f"isinstance(4, int)={isinstance(4, int)}, isinstance(4, object)={isinstance(4, object)}")

Note:

  • righe 2-3: una classe [Object] vuota;
  • Riga 2: La classe può essere dichiarata in tre forme:
    • class Object;
    • class Object();
    • class Object(object);
  • riga 3: un'altra forma di commento. Questo, preceduto da tre "", può estendersi su più righe;
  • riga 7: istanziazione della classe Object. Il risultato è un indirizzo, come mostrato nelle righe 24–26;
  • righe 8–9: inizializzazione diretta di due attributi dell'oggetto;
  • riga 17: copia dei riferimenti. Le variabili obj1 e obj2 sono due puntatori (riferimenti) allo stesso oggetto;
  • riga 19: modifichiamo l'oggetto puntato da [obj2]. Poiché [obj1] e [obj2] puntano allo stesso oggetto, la visualizzazione degli oggetti [obj1, obj2] nelle righe 21 e 22 mostrerà che l'oggetto puntato da [obj1] è cambiato;
  • righe 24–26: queste righe hanno lo scopo di dimostrare che le variabili [obj1] e [obj2] sono uguali. L'output della riga 26 lo confermerà. In questo confronto, sono gli indirizzi di [obj1] e [obj2] ad essere uguali;
  • Ogni oggetto Python è identificato da un ID univoco ottenuto utilizzando l'espressione [id(object)]. Le righe 24 e 25 mostreranno che gli ID degli oggetti a cui puntano [obj1] e [obj2] sono identici, dimostrando così che questi due riferimenti puntano allo stesso oggetto;
  • Righe 27–29: La funzione [isinstance(expr, Type)] restituisce il valore booleano True se l'espressione [expr] è di tipo [Type]. Qui vedremo che [obj1] è di tipo [Object], il che sembra naturale, ma anche di tipo [object]. La classe [object] è la classe padre di tutte le classi Python. Per la proprietà dell'ereditarietà delle classi, una classe figlia F possiede tutte le proprietà della sua classe padre P, e la funzione [isinstance(istanza di F, P)] restituisce True;
  • righe 30–32: mostrano che il tipo [int] è anche un tipo [object]. Tutti i tipi Python derivano dalla classe [object];

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_01.py
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, <class '__main__.Objet'>,2595221838768,un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
objet2=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
True
type(obj1)=<class '__main__.Objet'>
isinstance(obj1,Objet)=True, isinstance(obj1,object)=True
type(4)=<class 'int'>
isinstance(4, int)=True, isinstance(4, object)=True
 
Process finished with exit code 0

12.2. Script [classes_02]: una classe Person

Lo script [classes_02] dimostra che gli attributi di una classe sono pubblici: è possibile accedervi direttamente dall'esterno della classe. Questo è un altro esempio di utilizzo di una classe che non è raccomandato. Lo includiamo, tuttavia, perché potresti occasionalmente imbatterti in questo tipo di codice (Python lo consente) e devi essere in grado di comprenderlo.

#  person class
class Personne:
    #  class attributes
    #  undeclared - can be created dynamically

    #  method
    def identité(self: object) -> str:
        #  a priori, uses non-existent attributes
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  attributes are public and can be created dynamically
#  class instantiation
p = Personne()
#  direct initialization of class attributes
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
#  call a method of the
print(f"personne={p.identité()}")
#  instance type p
print(f"type(p)={type(p)}")
print(f"isinstance(Personne)={isinstance(p, Personne)}, isinstance(object)={isinstance(p, object)}")

Note:

  • righe 2–9: una classe con un metodo;
  • riga 7: ogni metodo di una classe deve avere l'oggetto self, che fa riferimento all'oggetto corrente, come primo parametro. Il metodo [identity] restituisce una stringa;
  • riga 15: istanziazione di un oggetto [Person];
  • righe 16–19: mostrano che gli attributi dell'oggetto possono essere creati dinamicamente (non esistono nella definizione della classe);
  • riga 9: gli attributi della classe sono indicati con la notazione [self.attribute];
  • righe 23–24 dimostrano che l'oggetto [p] è sia un'istanza della classe [Person] che della classe [object];

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_02.py
personne=[Paul,de la Hûche,48]
type(p)=<class '__main__.Personne'>
isinstance(Personne)=True, isinstance(object)=True
 
Process finished with exit code 0

12.3. Script [classes_03]: la classe Person con un costruttore

Lo script [classes_03] illustra l'uso standard di una classe:

#  person class
class Personne:
    #  builder - initializes three attributes
    def __init__(self: object, prénom: str, nom: str, âge: int):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  renders class attributes in string form
    def identité(self: object) -> str:
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  a Person object
p = Personne("Paul", "de la Hûche", 48)
#  method call
print(f"personne={p.identité()}")

Note:

  • riga 4: il costruttore della classe si chiama __init__. Come per gli altri metodi, il suo primo parametro è self;
  • riga 20: viene creato un oggetto Person utilizzando il costruttore della classe;
  • righe 13–15: il metodo [identity] restituisce una stringa che rappresenta il contenuto dell'oggetto;
  • riga 22: visualizza l'identità della persona;

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_03.py
personne=[Paul,de la Hûche,48]
 
Process finished with exit code 0

12.4. Script [classes_04]: metodi statici

Definiamo la seguente classe [Utils] (utils.py) nella cartella [modules]:

Image

class Utils:
    #  static method
    @staticmethod
    def is_string_ok(string: str) -> bool:
        #  string is a string
        erreur = not isinstance(string, str)
        if not erreur:
            #  is the chain empty?
            erreur = string.strip() == ''
        #  result
        return not erreur

Note

  • Riga 3: L'annotazione [@staticmethod] indica che il metodo contrassegnato con essa è un metodo di classe, non un metodo di istanza. Ciò è evidente dal fatto che il primo parametro del metodo annotato non è la parola chiave [self]. Pertanto, il metodo statico non ha accesso agli attributi dell'oggetto. Invece di scrivere:
u=Utils()
print(u.is_string_ok("abcd")

scriviamo

print(Utils.is_string_ok("abcd")

Poiché abbiamo scritto [Utils.is_string_ok] sopra, il metodo [is_string_ok] è chiamato metodo di classe (la classe Utils in questo caso). Per ottenere questo risultato, il metodo [Utils.is_string_ok] deve essere annotato con la parola chiave [@staticmethod].

Il metodo statico [Utils.is_string_ok] ci permette di verificare in questo caso che un dato sia una stringa non vuota.

Lo script [classes_04] utilizza la classe [Utils] come segue:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils

print(Utils.is_string_ok("  "))
print(Utils.is_string_ok(47))
print(Utils.is_string_ok(" q "))
  • Righe 1-4: Utilizziamo uno script di configurazione;

Lo script di configurazione [config.py] è il seguente:

def configure():
    import os

    #  configuration file folder
    script_dir = os.path.dirname(os.path.abspath(__file__))

    #  absolute paths of folders to put in the syspath
    absolute_dependencies = [
        f"{script_dir}/shared",
        "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages",
        "C:/myprograms/Python38/lib",
        "C:/myprograms/Python38/DLLs"
    ]

    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • Riga 9: La cartella [shared] verrà aggiunta al Python Path;

Il risultato dell'esecuzione è il seguente:

1
2
3
4
5
6
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_04.py
False
False
True

Process finished with exit code 0

12.5. Script [classes_05]: Controlli di validità degli attributi

Lo script [classes_05] introduce nuovi concetti:

  • definizione di un tipo di eccezione personalizzato;
  • definizione del metodo [_str_], che è il metodo di identità predefinito per le classi;
  • definizione delle proprietà;
#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  method toString of class
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    #  getters
    @property
    def prénom(self) -> str:
        return self.__prénom

    @property
    def nom(self) -> str:
        return self.__nom

    @property
    def âge(self) -> int:
        return self.__âge

    #  setters
    @prénom.setter
    def prénom(self, prénom: str):
        #  first name must be non-empty
        if Utils.is_string_ok(prénom):
            self.__prénom = prénom.strip()
        else:
            raise MyException("Le prénom doit être une chaîne de caractères non vide")

    @nom.setter
    def nom(self, nom: str):
        #  first name must be non-empty
        if Utils.is_string_ok(nom):
            self.__nom = nom.strip()
        else:
            raise MyException("Le nom doit être une chaîne de caractères non vide")

    @âge.setter
    def âge(self, âge: int):
        #  age must be an integer >=0
        erreur = False
        if isinstance(âge, int):
            if âge >= 0:
                self.__âge = âge
            else:
                erreur = True
        else:
            erreur = True
        #  mistake?
        if erreur:
            raise MyException("L'âge doit être un entier >=0")


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  you cannot access the __attr private attributes of the
p.__prénom = "Gaëlle"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")
p.prénom = "Sébastien"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")

Note:

  • righe 10–13: una classe MyException derivata dalla classe BaseException (tratteremo questo punto più avanti). Non aggiunge alcuna funzionalità a quest'ultima. È presente solo per fornire un'eccezione personalizzata;
  • riga 19: il costruttore ha valori predefiniti per i suoi parametri. Pertanto, l'operazione p = Person() è equivalente a p = Person("x", "y", 0);
  • righe 34–45: le proprietà della classe. Si tratta di metodi annotati con la parola chiave [@property]. Sono utilizzati per impostare i valori degli attributi;
  • righe 47–77: i setter della classe. Si tratta di metodi annotati con la parola chiave [@attributsetter]. Sono utilizzati per impostare il valore degli attributi;
  • righe 48–54: il setter per l'attributo [first_name]. Questo metodo verrà chiamato ogni volta che viene assegnato un valore all'attributo [first_name]:
p=Personne(…)
p.prénom=valeur

La riga 2 attiverà la chiamata [p.firstName(value)]. Il vantaggio di utilizzare un setter per assegnare un valore a un attributo è che, poiché il setter è una funzione, possiamo verificare la validità del valore assegnato all'attributo;

  • riga 51: verifichiamo che il valore assegnato all'attributo [first_name] sia una stringa non vuota. A tal fine, utilizziamo il metodo statico [Utils.isStringOk] visto in precedenza;
  • riga 52: il valore assegnato all'attributo [first_name] viene privato degli spazi iniziali e finali e assegnato all'attributo [self.__first_name]. Pertanto, l'attributo [first_name] stesso non viene utilizzato qui. Non avremmo potuto fare altrimenti, altrimenti avremmo avuto una chiamata ricorsiva infinita. Avremmo potuto utilizzare qualsiasi nome di attributo. Il fatto che abbiamo utilizzato l'attributo [__prénom] con due trattini bassi all'inizio dell'identificatore ha un significato speciale: gli attributi preceduti da due trattini bassi sono privati della classe. Ciò significa che non sono visibili dall'esterno della classe. Pertanto, non possiamo scrivere:
p=Personne(…)
p.__prénom=valeur

In realtà, vedremo presto che è possibile scriverlo, ma non modifica il nome. Fa qualcos’altro;

  • righe 53–54: se il valore assegnato a first_name è errato, viene generata un'eccezione. In questo modo, il codice chiamante saprà che la sua chiamata è errata;
  • righe 35–37: la proprietà [first_name]. Verrà chiamata ogni volta che [p.first_name] viene scritto in un'espressione. Verrà quindi chiamato il metodo [p.first_name()]. Riga 37: restituiamo il valore dell'attributo [__first_name], poiché abbiamo visto che il setter per l'attributo [first_name] assegna il suo valore all'attributo privato [__first_name];
  • Righe 56–62: il setter per l'attributo [last_name] è costruito in modo simile a quello dell'attributo [first_name]. Lo stesso vale per il setter dell'attributo [age] alle righe 64–77;
  • sebbene le proprietà [first_name, last_name, value] non siano gli attributi effettivi — che sono in realtà [__first_name, __last_name, __age] — continueremo a riferirci a loro come attributi della classe, poiché vengono utilizzati come tali;
  • righe 19–28: Il costruttore della classe utilizza implicitamente i setter per gli attributi [first_name, last_name, age]. Infatti, scrivendo [self.first_name = first_name] alla riga 26, viene implicitamente chiamato il metodo [first_name(self, first_name)]. Verrà quindi verificata la validità del parametro [first_name]. Lo stesso vale per gli altri due attributi [last_name, age];
  • con questo modello, non è possibile assegnare valori errati agli attributi della classe [first_name, last_name, age];
  • righe 30–32: la funzione __str__ sostituisce il metodo precedentemente chiamato identity. Il nome [__str__] (due trattini bassi prima e dopo) non è insignificante. Lo vedremo più avanti;
  • righe 83–86: istanziamento di una persona, seguito dalla visualizzazione della sua identità;
  • riga 84: istanziazione;
  • riga 86: visualizzazione. L'operazione richiede la visualizzazione dell'oggetto p come stringa. L'interprete Python chiama automaticamente il metodo p.__str__() se esiste. Questo metodo ha lo stesso scopo del metodo toString() nei linguaggi Java o .NET;
  • righe 87–89: gestione di una potenziale MyException. Quindi visualizza l'errore;
  • righe 91–99: come sopra per una seconda persona istanziata con parametri errati;
  • righe 102–109: come sopra per una terza persona istanziata con parametri predefiniti: non vengono passati parametri. Qui vengono quindi utilizzati i valori predefiniti di questi parametri nel costruttore;
  • righe 112–117: abbiamo detto che l'attributo [__first_name] era privato e quindi normalmente inaccessibile dall'esterno della classe. Vogliamo verificarlo;
  • righe 112–114: assegniamo un valore all'attributo [__first_name], quindi controlliamo i valori degli attributi [__first_name] e [first_name], che normalmente dovrebbero essere gli stessi;
  • righe 115–117: Ripetiamo l'operazione, questa volta inizializzando l'attributo [first_name];

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_05.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
personne=[x,y,0]
p.prénom=x
p.__prénom=Gaëlle
p.prénom=Sébastien
p.__prénom=Gaëlle
 
Process finished with exit code 0

Note

  • righe 5-6: vediamo che l'assegnazione [p.__first_name = "Gaëlle"] non ha modificato il valore dell'attributo [first_name], riga 5;
  • righe 7-8: vediamo che l'assegnazione [p.first_name = "Sébastien"] non ha modificato il valore dell'attributo [__first_name], riga 8;

Cosa possiamo concludere da questo? Che probabilmente l'operazione [p.__first_name = "Gaëlle"] ha creato un attributo pubblico [__first_name] per la classe, ma che questo è diverso dall'attributo privato [__first_name] manipolato al suo interno;

12.6. Script [classes_06]: Aggiunta di un metodo di inizializzazione dell'oggetto

Lo script [classes_06] aggiunge un metodo alla classe [Person]:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  other initialization method
    def init_with_personne(self: object, p: object):
        #  initializes the current object with a person p
        self.__init__(p.prénom, p.nom, p.âge)

    #  method toString of class
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    #  getters
    

    #  setters
    


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another Person obtained by recopying
try:
    #  instantiation Person class
    p2 = Personne()
    p2.init_with_personne(p)
    #  object display p2
    print(f"p2={p2}")
except MyException as erreur:
    #  error msg display
    print(erreur)

Note:

  • La differenza rispetto allo script precedente si trova nelle righe 30–33. Abbiamo aggiunto il metodo initWithPerson. Questo metodo chiama il costruttore __init__. A differenza dei linguaggi tipizzati, non è possibile avere metodi con lo stesso nome che si distinguono per la natura dei loro parametri o dei loro valori di ritorno. Pertanto, non è possibile avere più costruttori che creerebbero l'oggetto a partire da parametri diversi, in questo caso un oggetto di tipo Person;

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_06.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
p=[x,y,0]
p2=[x,y,0]
 
Process finished with exit code 0

12.7. Script [classes_07]: un elenco di oggetti Person

Ora inseriremo le classi [MyException] e [Person] in un modulo in modo da poterle utilizzare senza dover copiare il loro codice:

Image

Entrambe le classi saranno nel modulo [myclasses.py] sopra.

Lo script [classes_07] mostra che possiamo avere un elenco di oggetti:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne

#  ---------------------------------- main
#  create a list of person objects
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

Note:

  • riga 7: importiamo la classe [Person];
  • riga 11: un elenco di oggetti di tipo [Person];
  • righe 13–14: iteriamo attraverso questo elenco per visualizzare ciascuno dei suoi elementi;
  • riga 14: la funzione [print] visualizzerà la stringa che rappresenta l'oggetto [group[i]]. Per impostazione predefinita, verrà chiamato il metodo [__str__] di questi oggetti;

Risultati


C:\Users\serge\.virtualenvs\cours-python-v02\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/v-02/classes/01/classes_07.py
groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]
 
Process finished with exit code 0

12.8. Script [classes_08]: creazione di una classe derivata dalla classe Person

Definiamo la seguente classe [Teacher] nel modulo [myclasses]:


# classe Enseignant
class Enseignant(Personne):
    # constructeur
    def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
        # prénom : prénom de la personne
        # nom : nom de la personne
        # âge : âge de la personne
        # discipline : discpline enseignée
 
        # initialisation du parent
        Personne.__init__(self, prénom, nom, âge)
        # autres initialisations
        self.discipline = discipline
 
    # toString
    def __str__(self) -> str:
        return f"enseignant[{super().__str__()},{self.discipline}]"
 
    # propriétés
    @property
    def discipline(self) -> str:
        return self.__discipline
 
    @discipline.setter
    def discipline(self, discipline: str):
        # la discipline doit être une chaîne non vide
        if Utils.is_string_ok(discipline):
            self.__discipline = discipline
        else:
            raise MyException("La discipline doit être une chaîne de caractères non vide")
  • Riga 2: dichiara la classe Teacher come sottoclasse della classe Person. Una sottoclasse possiede tutte le proprietà (attributi e metodi) della sua classe padre oltre alle proprie;
  • riga 13: la classe [Teacher] definisce un nuovo attributo [subject];
  • riga 11: il costruttore della classe derivata Teacher deve chiamare il costruttore della classe padre Person, passando i parametri che si aspetta;
  • riga 17: la funzione [super()] chiama la classe padre. Qui, chiamiamo la funzione [__str__] della classe padre;
  • righe 19–30: vengono definiti il getter e il setter per il nuovo attributo [subject];

Lo script [classes_08] utilizza la classe [Teacher] come segue:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

Note:

  • riga 7: importiamo le classi [Person] e [Teacher] definite nel file [myclasses.py];
  • righe 11–14: definiamo un gruppo di persone e poi visualizziamo le loro identità;

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_08.py
groupe[0]=enseignant[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
 
Process finished with exit code 0

12.9. Script [classes_09]: seconda classe derivata dalla classe Person

Lo script [classes_09] introduce la classe [Student] derivata dalla classe [Person]. Questa è definita come segue nel modulo [myclasses]:


# classe Etudiant
class Etudiant(Personne):
    # constructeur
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0, formation: str = "x"):
        Personne.__init__(self, prénom, nom, âge)
        self.formation = formation
 
    # toString
    def __str__(self: object) -> str:
        return f"étudiant[{super().__str__()},{self.formation}]"
 
    # propriétés
    @property
    def formation(self) -> str:
        return self.__formation
 
    @formation.setter
    def formation(self, formation: str):
        # la formation doit être une chaîne non vide
        if Utils.is_string_ok(formation):
            self.__formation = formation
        else:
            raise MyException("La formation doit être une chaîne de caractères non vide")

Lo script [classes_09] utilizza la classe [Student] come segue:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant, Etudiant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
          Etudiant("Steve", "Boer", 22, "iup2 qualité")]
#  identity of these persons
for personne in groupe:
    #  person display
    print(personne)

Note:

  • Questo script è simile al precedente.

Risultati


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_09.py
enseignant[[Paul,Langevin,48],anglais]
[Sylvie,Lefur,70]
étudiant[[Steve,Boer,22],iup2 qualité]
 
Process finished with exit code 0

12.10. Script [classes_10]: la proprietà [__dict__]

Lo script [classes_10] introduce la proprietà [__dict__], che useremo spesso in seguito:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Etudiant

#  ---------------------------------- main
#  student creation
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
#  property dictionary
print(étudiant.__dict__)

Commenti

  • righe 1-4: l'applicazione viene configurata;
  • riga 7: viene importata la classe [Student];
  • riga 11: istanziamento di uno studente;
  • riga 13: uso del metodo predefinito [__dict__] (due trattini bassi prima e dopo l'identificatore);

I risultati sono i seguenti:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_10.py
{'_Personne__prénom': 'Steve', '_Personne__nom': 'Boer', '_Personne__âge': 22, '_Etudiant__formation': 'iup2 qualité'}

Process finished with exit code 0
  • alla riga 2, otteniamo un dizionario le cui chiavi sono le proprietà dell'oggetto precedute dal nome della classe a cui appartengono. Useremo questo dizionario per creare un collegamento tra l'oggetto e il dizionario;