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 un objeto es una instancia de una clase.

Image

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

12.1. Script [classes_01]: una clase Objeto

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 Objet;
    • class Objet() ;
    • class Objet(object);
  • línea 3: otra forma de comentario. Este, precedido por tres "", puede extenderse a varias líneas;
  • línea 7: instanciación de la clase Objet. El resultado es una dirección, como se verá 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] en las líneas 21 y 22 mostrarán que el objeto al que apunta [obj1] ha cambiado;
  • líneas 24-26: estas líneas tienen como objetivo mostrar la igualdad entre las variables [obj1] y [obj2]. La visualización de la línea 26 lo demostrará. En esta comparación, son las direcciones [obj1] y [obj2] las que son iguales;
  • cada objeto de 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 es 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] también es un tipo [object]. Todos los tipos de Python se 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. Este es otro ejemplo de uso de clases que no se recomienda. Sin embargo, lo presentamos porque a veces se puede encontrar este tipo de código (Python lo acepta) y, por lo tanto, es necesario poder entenderlo.


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

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


# ---------------------------------- main
# los atributos son públicos y pueden crearse 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 se pueden crear dinámicamente (no existen en la definición de la clase);
  • línea 9: los atributos de la clase se designan con 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: se muestra 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:
            # ¿La cadena está 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")

Como anteriormente se escribió [Utils.is_string_ok], el método [is_string_ok] se denomina método de clase (en este caso, la clase Utils). 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 sea 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; ya 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 se deben incluir en el 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"
    ]

    # Actualización del syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # se devuelve la configuración
    return {}
  • línea 9: la carpeta [shared] se colocará en la ruta de Python;

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]: verificaciones 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 predeterminado 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

        # almacenamiento 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

    # setter
    @prénom.setter
    def prénom(self, prénom: str):
        # el nombre debe ser válido
        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 debe ser distinto de 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 agrega 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. Por lo tanto, 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 verifica 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: al valor asignado al atributo [prénom] se le eliminan los «espacios en blanco» al inicio y al final de la cadena, y se le 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 de lo contrario 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 inicio 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 sí se puede escribir así, 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 esta manera, el código que realiza la llamada sabrá que su llamada es incorrecta;
  • líneas 35-37: la propiedad [prénom]. Se invocará cada vez que se escriba [p.prénom] en una expresión. En ese caso, se invocará el método [p.prénom()]. En la línea 37, se devuelve el valor del atributo [__prénom], ya que se observó 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 manera 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 verdaderos atributos —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 implícitamente 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__ reemplaza al método que anteriormente se llamaba identité. El nombre [__str__] (dos guiones bajos al principio y al final) 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 que se muestre 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 cumple la misma función que el método toString() en Java o en los lenguajes .NET;
  • líneas 87-89: manejo 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 ocurre con 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 indicado que el atributo [__prénom] es 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 luego se verifica el valor de los atributos [__prénom] y [prénom], que normalmente son iguales;
  • líneas 115-117: se repite la operación, esta vez inicializando 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 puede deducir de esto? Que, probablemente, la operación [p.__prénom = "Gaëlle"] creó un atributo público [__prénom] en la clase, pero que este es diferente del atributo privado [__prénom] que se manipula dentro de ella;

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

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


# se configura la aplicación
import config

config = config.configure()

# El syspath está configurado; 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

        # almacenamiento 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}]"

    # métodos getter


    # 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 el script anterior se encuentra en las líneas 30 a 33. Se ha agregado el método initWithPersonne. Este invoca al constructor __init__. No es posible, como en los lenguajes tipados, tener métodos con el mismo nombre que se diferencien 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()

# se ha configurado la ruta del sistema; ya 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

En el módulo [myclasses], definimos la siguiente clase [Enseignant]:


# clase «Profesor»
class Enseignant(Personne):
    # constructor
    def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
        # nombre: nombre de la persona
        # apellido: apellido de la persona
        # edad: edad de la persona
        # materia: materia que imparte

        # inicialización del padre
        Personne.__init__(self, prénom, nom, âge)
        # otras iniciales
        self.discipline = discipline

    # toString
    def __str__(self) -> str:
        return f"enseignant[{super().__str__()},{self.discipline}]"

    # propiedades
    @property
    def discipline(self) -> str:
        return self.__discipline

    @discipline.setter
    def discipline(self, discipline: str):
        # la disciplina debe ser una cadena no vacía
        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, pasándole los parámetros que este 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; ya se pueden realizar las importaciones
from myclasses import Personne, Enseignant

# ---------------------------------- main
# creación de un array 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]:


# clase Estudiante
class Etudiant(Personne):
    # constructor
    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}]"

    # propiedades
    @property
    def formation(self) -> str:
        return self.__formation

    @formation.setter
    def formation(self, formation: str):
        # la formación debe ser una cadena no vacía
        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; ya se pueden realizar las importaciones
from myclasses import Personne, Enseignant, Etudiant

# ---------------------------------- main
# creación de un array 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 con frecuencia en lo sucesivo:


# se configura la aplicación
import config

config = config.configure()

# El syspath está configurado; ya 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: se crea una instancia 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
  • en la 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 establecer un puente entre el objeto y el diccionario;