jueves, 15 de septiembre de 2016

Programación Python XV (clases)

Este es otro post dedicado a programación en Python, encontraréis todos los posts de este tipo juntos en el apartado Programación del blog. Y si queréis aprender más podéis visitar la web oficial de www.python.org

En este post vamos a ver como usar clases con python. Para pequeños programas generalmente no es necesario el uso de clases, pero si queréis desarrollar un programa algo más complejo las clases ayudan a organizar y simplificar el código.




1.1 Qué es una clase 

En los programas de cierta envergadura lo mas normal para trabajar en un lenguaje de programación es el uso de clases. Una clase nos sirve para crear un objeto con un conjunto de propiedades y/o funciones para trabajar con estas propiedades, con el objetivo de simplificar y ahorrar tiempo de programación, ya que podremos crear un objeto de la clase en cualquier momento y llamar a sus funciones sin tener que repetir código cada vez. Las clases se pueden guardar como un archivo python y pueden ser importados de la misma forma que importamos los módulos, tal y como vimos en un post anterior, Python X (módulos o bibliotecas), es recomendable que si creamos un archivo para cada clase que generamos lo guardemos con el mismo nombre de la clase de esta forma es muy fácil transformar una clase en un módulo.
En los siguientes ejemplos os mostraré como crear una clase para una agenda de clientes.



1.2 Definir una clase



El primer paso es definir una clase y sus propiedades, os dejo un ejemplo y luego os comento paso a paso que significa cada línea, cuidado con las comillas y los guiones bajos ya que la definición de la clase debe crearse correctamente. Aquí tenéis un ejemplo

# definimos el nombre de la classe
class agendaClientes:
    # linea de documentacion de la clase
    '''Esta clase representa a un objeto de cliente'''
    # metodo constructor de la clase
    def __init__(self,nombre,apellido1,apellido2,direccion):
        self.nombre=nombre
        self.apellido1=apellido1
        self.apellido2=apellido2
        self.direccion=direccion

En este ejemplo hemos empezado definiendo la clase con su nombre, en este caso hemos creado la clase mediante class agendaClientes:, a partir de aquí las líneas están sangradas para indicar que el código pertenece a la clase agendaClientes. La siguiente línea es una línea de documentación y va entre 3 comillas simples o 3 comillas dobles, en este caso he usado comillas simples '''Esta clase representa a un objeto de cliente''', este texto sirve de información para saber para que sirve nuestra clase, mas adelante veremos como recuperar esta información. Seguidamente empezamos el método constructor de la clase con la línea def __init__(self,nombre,apellido1,apellido2,direccion):, con esta línea generamos las propiedades de la clase, hay que respetar los dobles guiones bajos delante y detrás de __init__ y la primera propiedad entre paréntesis simpre será self. Debajo de esta línea y con su correspondiente sangrado añadiremos las líneas de asignación de las propiedades con self.nombre=nombre, self.apellido1=apellido1, self.apellido2=apellido2 y self.direccion=direccion. Es muy importante no olvidar la palabra self. delante de cada parámetro y método de la clase, de esta forma creamos en este ejemplo unas propiedades (nombre, apellido1, apellido2 y direccion) accesibles para todos los miembros de la clase.


1.3 Llamar a una clase



Una vez definida la clase podemos llamarla y crear un objeto de la clase y asignar valores a sus propiedades. Pero vamos por pasos, en primer lugar querremos saber para que sirve nuestra clase, para ello usaremos la siguiente orden.

print(agendaClientes.__doc__)

si ponemos este código sin sangrar a continuación del anterior, y ejecutamos el código, se mostrará por pantalla la línea de documentación (en el caso de que exista). Si tenemos muchas clases en nuestro programa guardadas cada una como un archivo python seguramente en algunos casos nos será útil crear una línea de documentación amplia, y es muy recomendable en el caso que estas clases vayan a ser usadas por terceros.

Ahora vamos a ver como generar un nuevo objeto de la clase y asignaremos valor a sus propiedades.

# creamos un objeto de la clase agendaClientes y asignamos valores
objetoAgenda=agendaClientes('Juan','Sanchez','Ortega','Avda. Los Pajaritos, 6')
# mostramos por pantalla el valor de las propiedddes del objeto
print(objetoAgenda.nombre)
print(objetoAgenda.apellido1)
print(objetoAgenda.apellido2)
print(objetoAgenda.direccion)

en este ejemplo he creado un objetoAgenda de la clase agendaClientes asignando directamente valor a las propiedades que luego he mostrado por pantalla. Podéis añadir este código sin sangrar a continuación del código anterior para ver el resultado.
También podríamos crear el objeto con las propiedades en blanco directamente y añadirlas o modificarlas luego, para ello usaríamos el siguiente código

# creamos un objeto de la clase agendaClientes
objetoAgenda2=agendaClientes('','','','')
# asignamos valor a las propiedades  del objeto
objetoAgenda2.nombre='Maria'
objetoAgenda2.apellido1='Garcia'
objetoAgenda2.apellido2='Requena'
objetoAgenda2.direccion='Calle del sol, 201'
# mostramos por pantalla el valor de las propiedades del objeto
print(objetoAgenda2.nombre)
print(objetoAgenda2.apellido1)
print(objetoAgenda2.apellido2)
print(objetoAgenda2.direccion)

Podéia añadir este código a continuación del anterior (sin sangrar para ver como funciona)


1.4 Definir un método en una clase



En el primer ejemplo hemos visto como definir una clase y sus propiedades, pero en una clase también podemos añadir métodos o funciones. Los métodos o funciones de la clase nos sirven para realizar acciones o procesos con los datos. Además los métodos de una clase pueden llamar a otros métodos de la misma clase (siempre que esté precedido de self.). Si no estáis familiarizados con las funciones aquí tenéis la referencia a un post Programación Python VI (funciones). Vamos a ver un ejemplo en el que definimos la misma clase que en el primer ejemplo y añadiremos dos funciones 

# definimos el nombre de la classe
class agendaClientes:
    # linea de documentacion de la clase
    '''Esta clase representa a un objeto de cliente'''
    # metodo constructor de la clase
    def __init__(self,nombre,apellido1,apellido2,direccion):
        self.nombre=nombre
        self.apellido1=apellido1
        self.apellido2=apellido2
        self.direccion=direccion
    # definimos una funcion que devuelve el nombre completo
    def nombre_completo(self):
        return self.nombre + ' ' + self.apellido1 + ' ' + self.apellido2
    # definimos una funcion que muestra por pantalla el nombre completo
    def imprimir_nombre(self):
        print(self.nombre_completo())

en este ejemplo hemos definido la clase agendaClientes pero hemos añadido dos funciones o métodos. La primera función es nombre_completo, cada vez que llamemos a esta función esta nos devolverá en nombre completo del cliente, gracias a la instrucción return self.nombre + ' ' + self.apellido1 + ' ' + self.apellido2. La segunda función lo que hace es mostrar por la pantalla el nombre completo y para ello usamos print() y llamamos a la primera función que nos devolvía el nombre completo para no tener que repetir de nuevo el código que concatena el nombre completo del cliente.

Para poder ver en funcionamiento estos métodos vamos a crear un objeto de la clase, añadiremos valor a los atributos y llamaremos a los métodos. Este sería el código

# creamos un objeto de la clase agendaClientes y asignamos valores
objetoAgenda=agendaClientes('Juan','Sanchez','Ortega','Avda. Los Pajaritos, 6')
# llamamos a la funcion imprimir_nombre
objetoAgenda.imprimir_nombre()

en el ejemplo hemos creado el objetoAgenda de la clase agendaClientes y hemos asignado valor a sus atributos, luego hemos llamado a la función imprimir_nombre() que muestra por pantalla el nombre completo (recordad que la función imprimir_nombre() llama a su vez a la función nombre_completo())


1.5 Herencia de clases



La herencia de clases sirve para crear subclases de una clase principal, a las que se añaden propiedades y/o métodos particulares. Por defecto una subclase hereda todas las propiedades y métodos de la clase principal, y adquiere otros nuevos que la complementan. Por ejemplo vamos a crear una subclase de agendaClientes que sera agendaClientesVIP a la que añadiremos dos propiedades numeroCliente y empresaCliente. Aquí tenéis el código completo (primero definimos la clase agendaClientes y luego la subclase agendaClientesVIP)

# definimos el nombre de la classe
class agendaClientes:
    # linea de documentacion de la clase
    '''Esta clase representa a un objeto de cliente'''
    # metodo constructor de la clase
    def __init__(self,nombre,ape1,ape2,direccion):
        self.nombre=nombre
        self.ape1=ape1
        self.ape2=ape2
        self.direccion=direccion
    # definimos una funcion que devuelve el nombre completo
    def nombre_completo(self):
        return self.nombre + ' ' + self.ape1 + ' ' + self.ape2
    # definimos una funcion que muestra por pantalla el nombre completo
    def imprimir_nombre(self):
        print(self.nombre_completo())

# definimos una subclase heredada de agendaClientesVIP
class agendaClientesVIP (agendaClientes):
    # linea de documentacion de la clase
    ''''Esta es la subclase de clientes VIP'''
    # metodo constructor de la clase agendaClientesVIP
    def __init__(self,nombre,ape1,ape2,direccion,numeroCliente,empresaCliente):
        # metodo constructor de la clase principal agendaClientes
        super().__init__(nombre,ape1,ape2,direccion)
        # definición de las propiedades particulares de la clase agendaClientesVIP
        self.numeroCliente=numeroCliente
        self.empresaCliente=empresaCliente

atención para la versión de python3 hemos usado la línea 

super().__init__(nombre,ape1,ape2,direccion) 

que nos sirve para llamar al método constructor de la clase principal, pero en la versión de python2 tendremos que usar el nombre de la clase principal en lugar de super(), tal y como os muestro a continuación

agendaClientes().__init__(nombre,apellido1,apellido2,direccion)

una vez hemos guardado el código de esta clase heredada podemos crear un objeto de esta de la misma forma que lo hemos hecho antes y ver su funcionamiento

# creamos el objeto clienteVIP de la clase agendaClientesVIP
clienteVIP=agendaClientesVIP('','','','','','')
# añadimos valor a las propiedades de la clase
clienteVIP.nombre='Juan'
clienteVIP.ape1='Soto'
clienteVIP.ape2='Romero'
clienteVIP.direccion='Calle del bosque, 23'
clienteVIP.numeroCliente='00024'
clienteVIP.empresaCliente='Limpiezas SoRo SA'
# llamamos a un método de la clase
clienteVIP.imprimir_nombre()

con este código hemos creado un objeto de una clase (agendaClientesVIP), que hereda de una clase principal (agendaClientes). Seguidamente hemos dado valor a las propiedades de la clase (las heredadas de la clase principal y las particulares), y finalmente hemos llamado a un método de la clase.

Las clases pueden complicarse mucho mas, pero recordad que sirven para facilitar la programación, aunque parezcan complicadas, en proyectos de cierta envergadura son muy útiles y de gran ayuda