Skip to content

12. Las clases y los objetos

La clase es el molde a partir del cual se crean los objetos. Se dice que el objeto es una instancia de una clase.

Image

Nota: el archivo [shared] se ha colocado en el directorio [Sources Root] del proyecto.

12.1. Script [classes_01]: una clase Objet

El script [classes_01] muestra un uso obsoleto de las clases:


# una clase
class Objet(object):
    """une classe Objet vide"""


# cualquier variable de tipo [Objet] puede tener atributos por definición
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
# muestra el objeto
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
# modifica el objeto
obj1.attr2 += 100
# muestra el objeto
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
# asigna la referencia obj1 a obj2
obj2 = obj1
# modifica el objeto al que apunta obj2
obj2.attr2 = 0
# muestra los dos objetos: obj1 ahora apunta a un objeto modificado
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
# obj1 y obj2 apuntan al mismo objeto
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
# tipo de la instancia obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
# todo tipo es un objeto en Python
print(f"type(4)={type(4)}")
print(f"isinstance(4, int)={isinstance(4, int)}, isinstance(4, object)={isinstance(4, object)}")

Notas:

  • líneas 2-3: una clase [Objet] vacía;
  • línea 2: la clase se puede declarar de tres formas:
    • class Objeto;
    • class Objet() ;
    • class Objet(object);
  • línea 3: otra forma de comentario. Este, precedido de tres «, puede extenderse a lo largo de varias líneas;
  • línea 7: instanciación de la clase Objet. El resultado es una dirección, como se muestra en las líneas 24-26;
  • líneas 8-9: inicialización directa de dos atributos del objeto;
  • línea 17: copia de referencias. Las variables obj1 y obj2 son dos punteros (referencias) a un mismo objeto;
  • línea 19: se modifica el objeto al que apunta [obj2]. Dado que [obj1] y [obj2] apuntan al mismo objeto, las visualizaciones de los objetos [obj1, obj2] de las líneas 21 y 22 mostrarán que el objeto al que apunta [obj1] ha cambiado;
  • líneas 24-26: estas líneas pretenden mostrar la igualdad de las variables [obj1] y [obj2]. La visualización de la línea 26 lo mostrará. En esta comparación, son las direcciones [obj1] y [obj2] las que son iguales;
  • cada objeto Python se identifica mediante un número único que se obtiene con la expresión [id(objet)]. Las líneas 24 y 25 mostrarán que los números de los objetos a los que apuntan [obj1] y [obj2] son idénticos, lo que demuestra que estas dos referencias apuntan al mismo objeto;
  • líneas 27-29: la función [isinstance(expr,Type)] devuelve el valor booleano True si la expresión [expr] es de tipo [Type]. Aquí veremos que [obj1] es de tipo [Objet], lo cual parece natural, pero también de tipo [object]. La clase [object] es la clase padre de todas las clases de Python. Por la propiedad de la herencia de clases, una clase hija F tiene todas las propiedades de su clase padre P y la función [isinstance(instance de F, P)] devuelve True;
  • líneas 30-32: muestran que el tipo [int] es también un tipo [object]. Todos los tipos de Python derivan de la clase [object];

Resultados


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 clase Persona

El script [classes_02] muestra que los atributos de una clase son públicos: se puede acceder a ellos directamente desde fuera de la clase. Se trata, una vez más, de un uso de las clases que no se recomienda. No obstante, lo incluimos porque a veces se puede encontrar este tipo de código (Python lo acepta) y, por lo tanto, hay que ser capaz de entenderlo.


# clase Persona
class Personne:
    # atributos de la clase
    # no declarados: se pueden crear dinámicamente

    # método
    def identité(self: object) -> str:
        # a priori, utiliza atributos inexistentes
        return f"[{self.prénom},{self.nom},{self.âge}]"


# ---------------------------------- main
# los atributos son públicos y se pueden crear dinámicamente
# instanciación de clase
p = Personne()
# inicialización directa de atributos de la clase
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
# llamada a un método de la clase
print(f"personne={p.identité()}")
# tipo de la instancia p
print(f"type(p)={type(p)}")
print(f"isinstance(Personne)={isinstance(p, Personne)}, isinstance(object)={isinstance(p, object)}")

Notas:

  • líneas 2-9: una clase con un método;
  • línea 7: todo método de una clase debe tener como primer parámetro el objeto self, que designa al objeto actual. El método [identité] devuelve una cadena de caracteres;
  • línea 15: instanciación de un objeto [Personne];
  • líneas 16-19: muestran que los atributos del objeto pueden crearse dinámicamente (no existen en la definición de la clase);
  • línea 9: los atributos de la clase se designan mediante la notación [self.attribut];
  • las líneas 23-24 mostrarán que el objeto [p] es a la vez una instancia de la clase [Personne] y de la clase [object];

Resultados


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 clase Persona con un constructor

El script [classes_03] muestra el uso habitual de una clase:


# clase Persona
class Personne:
    # constructor: inicializa tres atributos
    def __init__(self: object, prénom: str, nom: str, âge: int):
        # nombre: nombre de la persona
        # apellido: apellido de la persona
        # edad: edad de la persona

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

    # devuelve los atributos de la clase en forma de cadena de caracteres
    def identité(self: object) -> str:
        return f"[{self.prénom},{self.nom},{self.âge}]"


# ---------------------------------- mano
# un objeto Persona
p = Personne("Paul", "de la Hûche", 48)
# llamada a un método
print(f"personne={p.identité()}")

Notas:

  • línea 4: el constructor de una clase se llama __init__. Al igual que con los demás métodos, su primer parámetro es self;
  • línea 20: se crea un objeto Personne con el constructor de la clase;
  • líneas 13-15: el método [identité] devuelve una cadena de caracteres que representa el contenido del objeto;
  • línea 22: visualización de la identidad de la persona;

Resultados


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]: métodos estáticos

Definimos en la carpeta [modules] la siguiente clase [Utils] (utils.py):

Image


class Utils:
    # método estático
    @staticmethod
    def is_string_ok(string: str) -> bool:
        # ¿Es string una cadena?
        erreur = not isinstance(string, str)
        if not erreur:
            # ¿Es la cadena vacía?
            erreur = string.strip() == ''
        # resultado
        return not erreur

Notas

  • línea 3: la anotación [@staticmethod] indica que el método así anotado es un método de clase y no un método de instancia. Esto se observa en el hecho de que el primer parámetro del método así anotado no es la palabra clave [self]. Por lo tanto, el método estático no tiene acceso a los atributos del objeto. En lugar de escribir:
u=Utils()
print(u.is_string_ok("abcd")

se escribe

print(Utils.is_string_ok("abcd")

Dado que anteriormente se ha escrito [Utils.is_string_ok], el método [is_string_ok] se denomina método de clase (la clase Utils en este caso). Para ello, el método [Utils.is_string_ok] debe anotarse con la palabra clave [@staticmethod].

El método estático [Utils.is_string_ok] permite aquí verificar que un dato es una cadena de caracteres no vacía.

El script [classes_04] utiliza la clase [Utils] de la siguiente manera:


# se configura la aplicación
import config

config = config.configure()

# el syspath está configurado; se pueden realizar las importaciones
from utilitaires import Utils

print(Utils.is_string_ok("  "))
print(Utils.is_string_ok(47))
print(Utils.is_string_ok(" q "))
  • líneas 1-4: se utiliza un script de configuración;

El script de configuración [config.py] es el siguiente:


def configure():
    import os

    # carpeta del archivo de configuración
    script_dir = os.path.dirname(os.path.abspath(__file__))

    # rutas absolutas de las carpetas que hay que añadir a la ruta de sistema
    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"
    ]

    # actualización de la ruta del sistema
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # se devuelve el config
    return {}
  • línea 9: la carpeta [shared] se colocará en el Python Path;

El resultado de la ejecución es el siguiente:

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]: comprobaciones de validez de los atributos

El script [classes_05] introduce nuevos conceptos:

  • definición de un tipo de excepción propio;
  • definición del método [_str_], que es el método de identidad por defecto de las clases;
  • definición de propiedades;

# se configura la aplicación
import config

config = config.configure()

# el syspath está configurado: ya se pueden realizar las importaciones
from utilitaires import Utils


# una clase de excepción propia derivada de [BaseException]
class MyException(BaseException):
    # no se hace nada: clase vacía
    pass


# clase Persona
class Personne:
    # constructor
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        # nombre: nombre de la persona
        # apellido: apellido de la persona
        # edad: edad de la persona

        # memorización de los parámetros
        # las inicializaciones se realizarán mediante los setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    # método toString de la clase
    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):
        # el nombre no debe estar vacío
        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):
        # el nombre no debe estar vacío
        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):
        # la edad debe ser un número entero >=0
        erreur = False
        if isinstance(âge, int):
            if âge >= 0:
                self.__âge = âge
            else:
                erreur = True
        else:
            erreur = True
        # ¿error?
        if erreur:
            raise MyException("L'âge doit être un entier >=0")


# ---------------------------------- main
# un objeto Persona
try:
    # instanciación de la clase Persona
    p = Personne("Paul", "de la Hûche", 48)
    # visualización del objeto p
    print(f"personne={p}")
except MyException as erreur:
    # visualización del error
    print(erreur)

# otro objeto Persona
try:
    # instanciación de la clase Persona
    p = Personne("xx", "yy", "zz")
    # visualización del objeto p
    print(f"personne={p}")
except MyException as erreur:
    # visualización de error
    print(erreur)

# otra persona, esta vez sin parámetros
try:
    # instanciación de la clase Persona
    p = Personne()
    # visualización del objeto p
    print(f"personne={p}")
except MyException as erreur:
    # visualización del mensaje de error
    print(erreur)

# no se puede acceder a los atributos privados __attr de la clase
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}")

Notas:

  • líneas 10-13: una clase MyException derivada de la clase BaseException (veremos este punto más adelante). No añade ninguna funcionalidad a esta última. Solo está ahí para tener una excepción propia;
  • línea 19: el constructor tiene valores por defecto para sus parámetros. Así, la operación p=Personne() es equivalente a p=Personne("x","y",0);
  • líneas 34-45: las propiedades de la clase. Son métodos anotados con la palabra clave [@property]. Se utilizan para devolver el valor de los atributos;
  • líneas 47-77: los métodos setter de la clase. Son métodos anotados con la palabra clave [@attributsetter]. Se utilizan para establecer el valor de los atributos;
  • líneas 48-54: el setter del atributo [prénom]. Este método se llamará cada vez que se asigne un valor al atributo [prénom]:
p=Personne(…)
p.prénom=valeur

La línea 2 provocará la llamada a [p.prénom(valeur)]. La ventaja de utilizar un setter para asignar un valor a un atributo es que, al ser el setter una función, se puede verificar la validez del valor asignado al atributo;

  • línea 51: se comprueba que el valor asignado al atributo [prénom] sea una cadena de caracteres no vacía. Para ello se utiliza el método estático [Utils.isStringOk] visto anteriormente;
  • línea 52: el valor asignado al atributo [prénom] se limpia de los «espacios» al principio y al final de la cadena y se asigna al atributo [self.__prénom]. Por lo tanto, aquí no se utiliza el atributo [prénom]. No se podía hacer de otra manera, ya que se habría producido una llamada recursiva infinita. Se podría haber utilizado cualquier nombre de atributo. El hecho de haber utilizado el atributo [__prénom] con dos guiones bajos al principio del identificador tiene un significado especial: los atributos precedidos por dos guiones bajos son privados de la clase. Esto significa que no son visibles desde fuera de ella. Por lo tanto, no se puede escribir:
p=Personne(…)
p.__prénom=valeur

De hecho, pronto veremos que se puede escribir, pero que eso no modifica el nombre. Hace otra cosa;

  • líneas 53-54: si el valor asignado al nombre no es correcto, se lanza una excepción. De este modo, el código que realiza la llamada sabrá que su llamada es incorrecta;
  • líneas 35-37: la propiedad [prénom]. Se llamará cada vez que escribamos [p.prénom] en una expresión. Entonces se llamará al método [p.prénom()]. Línea 37: se devuelve el valor del atributo [__prénom], ya que se ha visto que el setter del atributo [prénom] asignaba su valor al atributo privado [__prénom];
  • líneas 56-62: el setter del atributo [nom] se construye de forma análoga al del atributo [prénom]. Lo mismo ocurre con el del atributo [âge] en las líneas 64-77;
  • aunque las propiedades [prénom, nom, valeur] no son los atributos reales, que en realidad son [__prénom, __nom, __âge], se seguirán denominando atributos de la clase, ya que se utilizan como tales;
  • líneas 19-28: el constructor de la clase utiliza de forma implícita los setters de los atributos [prénom, nom, âge]. De hecho, al escribir, en la línea 26, [self.prénom = prénom], se llamará implícitamente al método [prénom(self, prénom)]. A continuación, se verificará la validez del parámetro [prénom]. Lo mismo ocurrirá con los otros dos atributos [nom, âge];
  • con este modelo, no se pueden asignar valores incorrectos a los atributos [prénom, nom, âge] de la clase;
  • líneas 30-32: la función __str__ sustituye al método que antes se llamaba identité. El nombre [__str__] (dos guiones bajos delante y detrás) no es casual. Lo veremos más adelante;
  • líneas 83-86: instanciación de una persona y posterior visualización de su identidad;
  • línea 84: instanciación;
  • línea 86: visualización. La operación solicita mostrar a la persona p en forma de cadena de caracteres. El intérprete de Python llama entonces automáticamente al método p.__str__() si existe. Este método desempeña la misma función que el método toString() en Java o en los lenguajes .NET;
  • líneas 87-89: gestión de una posible excepción de tipo MyException. A continuación, muestra el error;
  • líneas 91-99: lo mismo para una segunda persona instanciada con parámetros erróneos;
  • líneas 102-109: lo mismo para una tercera persona instanciada con los parámetros por defecto: no se pasa ningún parámetro. En este caso se utilizan los valores por defecto de estos parámetros en el constructor;
  • líneas 112-117: se ha dicho que el atributo [__prénom] era privado, por lo que normalmente no es accesible desde fuera de la clase. Queremos verificarlo;
  • líneas 112-114: se asigna un valor al atributo [__prénom] y, a continuación, se comprueba el valor de los atributos [__prénom] y [prénom], que normalmente son iguales;
  • líneas 115-117: se repite la operación, inicializando esta vez el atributo [prénom];

Resultados


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

Notas

  • líneas 5-6: se observa que la asignación [p.__prénom = "Gaëlle"] no ha cambiado el valor del atributo [prénom], línea 5;
  • líneas 7-8: se observa que la asignación [p.prénom = "Sébastien"] no ha cambiado el valor del atributo [__prénom], línea 8;

¿Qué se deduce de esto? Que, probablemente, la operación [p.__prénom = "Gaëlle"] ha creado un atributo público [__prénom] en la clase, pero que este es diferente del atributo privado [__prénom] manipulado dentro de la misma;

12.6. Script [classes_06]: adición de un método de inicialización del objeto

El script [classes_06] añade un método a la clase [Personne]:


# se configura la aplicación
import config

config = config.configure()

# se ha configurado la ruta del sistema; se pueden realizar las importaciones
from utilitaires import Utils


# una clase de excepción propia derivada de [BaseException]
class MyException(BaseException):
    # no se hace nada: clase vacía
    pass


# clase Persona
class Personne:
    # constructor
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        # nombre: nombre de la persona
        # apellido: apellido de la persona
        # edad: edad de la persona

        # memorización de los parámetros
        # las inicializaciones se realizarán mediante los setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    # otro método de inicialización
    def init_with_personne(self: object, p: object):
        # inicializa el objeto actual con una persona p
        self.__init__(p.prénom, p.nom, p.âge)

    # método toString de la clase
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    # getters


    # setters



# ---------------------------------- main
# un objeto Persona
try:
    # instanciación de la clase Persona
    p = Personne("Paul", "de la Hûche", 48)
    # visualización del objeto p
    print(f"personne={p}")
except MyException as erreur:
    # visualización del mensaje de error
    print(erreur)

# otro objeto Persona
try:
    # instanciación de la clase Persona
    p = Personne("xx", "yy", "zz")
    # visualización del objeto p
    print(f"p={p}")
except MyException as erreur:
    # visualización del mensaje de error
    print(erreur)

# otra persona, esta vez sin parámetros
try:
    # instanciación de la clase Persona
    p = Personne()
    # visualización del objeto p
    print(f"p={p}")
except MyException as erreur:
    # visualización del mensaje de error
    print(erreur)

# otra persona obtenida por copia
try:
    # instanciación de la clase Persona
    p2 = Personne()
    p2.init_with_personne(p)
    # visualización del objeto p2
    print(f"p2={p2}")
except MyException as erreur:
    # visualización del mensaje de error
    print(erreur)

Notas:

  • la diferencia con respecto al script anterior se encuentra en las líneas 30-33. Se ha añadido el método initWithPersonne. Este invoca al constructor __init__. No es posible, como en los lenguajes tipados, tener métodos con el mismo nombre diferenciados por la naturaleza de sus parámetros o de su resultado. Por lo tanto, no es posible tener varios constructores que construyan el objeto a partir de parámetros diferentes, en este caso un objeto de tipo Personne;

Resultados


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]: una lista de objetos Persona

Ahora colocamos las clases [MyException] y [Personne] en un módulo para poder utilizarlas sin tener que copiar su código:

Image

Ambas clases se encontrarán en el módulo [myclasses.py] mencionado anteriormente.

El script [classes_07] muestra que se puede obtener una lista de objetos:


# se configura la aplicación
import config

config = config.configure()

# la ruta del sistema está configurada: se pueden realizar las importaciones
from myclasses import Personne

# ---------------------------------- main
# creación de una lista de objetos persona
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
# identidad de estas personas
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

Notas:

  • línea 7: se importa la clase [Personne];
  • línea 11: una lista de objetos de tipo [Personne];
  • líneas 13-14: se recorre esta lista para mostrar cada uno de sus elementos;
  • línea 14: la función [print] mostrará la cadena que representa el objeto [groupe[i]]. Por defecto, se llamará al método [__str__] de estos;

Resultados


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]: creación de una clase derivada de la clase Persona

Definimos en el módulo [myclasses] la siguiente clase [Enseignant]:


# 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")
  • línea 2: declara la clase Enseignant como una clase derivada de la clase Personne. Una clase derivada tiene todas las propiedades (atributos y métodos) de su clase padre, además de las propias;
  • línea 13: la clase [Enseignant] define un nuevo atributo [discipline];
  • línea 11: el constructor de la clase derivada Enseignant debe llamar al constructor de la clase padre Personne, transmitiéndole los parámetros que espera;
  • línea 17: la función [super()] devuelve la clase padre. Aquí se llama a la función [__str__] de la clase padre;
  • líneas 19-30: se definen el getter y el setter del nuevo atributo [discipline];

El script [classes_08] utiliza la clase [Enseignant] de la siguiente manera:


# se configura la aplicación
import config

config = config.configure()

# el syspath está configurado - se pueden realizar las importaciones
from myclasses import Personne, Enseignant

# ---------------------------------- main
# creación de una tabla de objetos Persona y derivados
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
# identidad de estas personas
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

Notas:

  • línea 7: se importan las clases [Personne] y [Enseignant] definidas en el archivo [myclasses.py];
  • líneas 11-14: se define un grupo de personas cuya identidad se muestra a continuación;

Resultados


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]: segunda clase derivada de la clase Persona

El script [classes_09] introduce la clase [Etudiant] derivada de la clase [Personne]. Esta se define de la siguiente manera en el módulo [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")

El script [classes_09] utiliza la clase [Etudiant] de la siguiente manera:


# se configura la aplicación
import config

config = config.configure()

# el syspath está configurado; se pueden realizar las importaciones
from myclasses import Personne, Enseignant, Etudiant

# ---------------------------------- principal
# creación de una tabla de objetos Persona y derivados
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
          Etudiant("Steve", "Boer", 22, "iup2 qualité")]
# identidad de estas personas
for personne in groupe:
    # visualización de la persona
    print(personne)

Notas:

  • este script es similar al anterior.

Resultados


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 propiedad [__dict__]

El script [classes_10] presenta la propiedad [__dict__], que utilizaremos a menudo a partir de ahora:


# se configura la aplicación
import config

config = config.configure()

# el syspath está configurado; se pueden realizar las importaciones
from myclasses import Etudiant

# ---------------------------------- main
# creación de un estudiante
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
# diccionario de propiedades
print(étudiant.__dict__)

Comentarios

  • líneas 1-4: se configura la aplicación;
  • línea 7: se importa la clase [Etudiant];
  • línea 11: instanciación de un estudiante;
  • línea 13: uso del método predefinido [__dict__] (2 caracteres subrayados antes y después del identificador);

Los resultados son los siguientes:

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
  • línea 2, se obtiene un diccionario cuyas claves son las propiedades del objeto precedidas por el nombre de la clase a la que pertenecen. Utilizaremos este diccionario para crear un puente entre el objeto y el diccionario;