El lado objeto de Python
Introducción
El capítulo anterior explica que el lenguaje Python se usa como cualquier otro lenguaje, es decir, una serie de instrucciones, posiblemente agrupadas en funciones, para que su ordenador haga lo que usted quiere.
Esto se conoce comúnmente como programación funcional o procedural.
Aunque esta técnica seguirá utilizándose durante mucho tiempo, hay otra forma de ver las cosas y esto abre todo un universo paralelo de posibilidades:
la programación orientada a objetos (POO) |
La POO
Con la programación procedural, el procesamiento a realizar se divide en funciones, con datos locales o globales, y el conjunto da como resultado un procedimiento que se debe realizar paso a paso.
La POO buscará más bien definir bloques de software (objetos), así como la forma en que se comportan.
Cada objeto es responsable de tareas y datos específicos, y es a través de la interacción de los objetos entre sí como tiene lugar el procesamiento.
Así, para desencadenar una acción, es necesario enviar un mensaje al objeto responsable de la acción con toda la información necesaria para llevarla a cabo.
Ejemplo
Capítulo12.Imprime(pagina=1) # Impresión de la página 1 del objeto Capítulo12
El objetivo es virtualizar elementos muy reales y simular un pequeño modelo del universo.
Esto requiere cambiar su forma de pensar, pero su uso es mucho más intuitivo.
Por el momento, puede parecer abstracto. Por eso es necessario definir un poco la materia prima de la POO: el objeto.
El objeto
Un objeto, en el sentido informático del término, es como cualquier otra entidad informática: una referencia a una asignación de memoria RAM.
Pero un objeto en POO es mucho más que una simple variable o función.
Un objeto agrupa valores y funciones.
Estos valores y funciones constituyen la estructura del objeto y se les asigna un nombre para diferenciarlos.
Un punto importante: un objeto es único, porque es serializado por el proceso de creación. Esto se detallará más adelante.
En POO, para diferenciarla de la programación "procedural tradicional", hablaremos más sobre métodos para las funciones y propiedades o atributos para las variables.
Sin embargo, a continuación, hablaremos de una función o un método sin distinción, sabiendo que un método es una función vinculada a un objeto o a una clase.
Otro punto: en POO, para facilitar el proceso de creación de objetos, comenzamos describiendo la matriz en la que se crearán, es decir, la "clase".
La clase
La clase describe las propiedades y métodos específicos de los objetos; sirve como modelo para la creación de objetos.
El término exacto para crear objetos en POO se llama "instanciación".
Recuerde este punto importante: todos los objetos son instancias de una clase.
Por lo tanto, para abordar la POO, comenzamos describiendo una clase, que luego instanciamos.
El resultado da objetos que se pueden utilizar para programar.
Una clase simple
Para crear una clase, use la instrucción class con la siguiente sintaxis:
class NombreDeLaClase:
bloque de código
La instrucción class va seguida del carácter dos puntos (’:’) y, como cualquier bloque de código Python, se define por su indentación.
Por convención, pero no es obligatorio, los nombres de las clases están en minúsculas excepto por la primera letra de cada palabra.
Ejemplo
ElNombreDeEstaClaseEsCorrecto, MiClase, MyClass...
A continuación se explica cómo crear la clase más simple posible con el lenguaje Python.
Inicie su terminal preferido, luego la línea de comando de Python y escriba las siguientes líneas:
class ClaseSimple:
pass
Y ya está.
Se obtendrá la sesión:
Python 3.7.3 (default, Apr 30 2019, 16:55:18)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class ClaseSimple:
... pass
...
>>>
La instrucción pass no hace nada.
Esta práctica instrucción se usa comúnmente para crear clases o funciones minimalistas, que se pueden completar más tarde.
Es una clase vacía, pero es una clase real, que podemos estudiar y especialmente instanciar.
Para hacer esto, simplemente llame a la clase como una función.
Ejemplo
NombreDeLaClase()
A continuación se explica cómo crear un objeto en Python desde nuestra clase "simple":
>>> mi_objeto = ClaseSimple()
Detrás de esta simplicidad se esconde el siguiente proceso:
-
creación de una referencia denominada "mi_objeto";
-
llamada del constructor de la clase;
-
devuelve un puntero al nuevo objeto que se asignará a la referencia mi_objeto.
En POO, el constructor es la función especial que se llama al crear una instancia del objeto; se llama __new__ en Python. Su uso se detalla un poco más adelante....
Añadimos atributos
Ahora veamos cómo ampliar nuestras clases con propiedades.
Mencionamos anteriormente la noción de constructor. Este, en POO, se encarga de asignar memoria para el objeto a crear y de inicializar sus atributos.
Para Python, hay dos funciones especiales muy distintas: __new__ y __init__.
¿Por qué hay dos guiones bajos en el nombre de estas funciones?
En Python, por convención, el guion bajo doble significa que este es un método "especial" reservado para el lenguaje Python.
Debido a su importancia, aquí solo se detallará la función __init__, la función __new__ se detallará en otro capítulo.
En cualquier caso, estas son funciones como cualquier otra, aunque solo existan en el contexto de la clase.
A continuación se muestra la sintaxis de la función __init__:
class <NombreDeLaClase>:
def __init__(<identificador>):
<declaraciones>
Explicación:
Definimos una clase.
Luego viene el método __init__, que debe declarar un parámetro para designar la instancia de la clase.
Este parámetro, generalmente llamado self, es una referencia que se debe declarar primero para los métodos (funciones vinculadas a una clase).
Este identificador representa el objeto actual y permite inicializar los atributos del objeto en la forma: <identificador>....
Un primer script ”objeto”
Retomamos nuestra clase Document en un script llamado Documento.py.
Estará de acuerdo en que es más fácil crear un archivo en concordancia con el nombre de la clase, aunque no es obligatorio.
Tomemos como base de trabajo el siguiente ejemplo:
##
## declaración de la clase
##
class Document:
def __init__(self):
self.titulo = ""
self.autor = ""
self.fecha_de_actualizacion = None
self.contenido = []
##
## para probar la clase
##
if __name__ == '__main__':
A = Document()
print(A)
En este script, describimos una clase (Documento), con atributos (titulo, autor, fecha_de_actualizacion y contenido) inicializados en la función __init__.
Luego, realizamos una prueba en la variable __name__; esta variable especial contiene el nombre del script actual.
Si el script actual es el script principal, entonces __name__ es igual al valor ‘__main__’.
Esto significa que estamos ejecutando el script principal (Documento.py); en este caso, se pueden ejecutar las instrucciones de prueba de la clase Document.
Esta prueba, en el presente caso, consiste en crear un objeto...
La sobrecarga de funciones
De hecho, cuando en una clase se declara una función especial __str__ o __init__, "sobrecargamos" la función en cuestión.
En POO, sobrecargar una función significa declarar una función del objeto "padre" en el objeto "hijo".
Como en la vida real, los objetos tienen un padre.
Así, el objeto hijo utiliza la función de su clase y ya no la de la clase "padre".
Las clases "hijas" heredan las propiedades y métodos de sus clases "madres".
Este mecanismo se llama "herencia" y es uno de los conceptos básicos de la POO.
La herencia
Al describir una clase, se definen métodos y propiedades, luego se instancia.
Pero no partimos de cero: todos los objectos creados con Python provienen de la clase object. Y durante la instanciación, todos los objectos heredan los atributos y métodos de esa clase.
Este concepto se llama herencia, que es uno de los pilares de la potencia de la POO.
En POO, es posible incluir en una clase las propiedades y métodos de otra, con el propósito de crear una jerarquía de clases y especializar las clases en cada nivel.
Con este concepto, podemos crear clases, instanciar objetos y reutilizar esas clases a través de la herencia.
Esto nos permite enriquecer las clases hijo redefiniendo (sobrecarga) funciones o añadiendo atributos, y así obtener el comportamiento deseado.
A continuación se muestra la sintaxis de herencia en Python:
class NombreDeLaClase( clase madre [, otra clase madre ...] )
Como puede ver, en Python la herencia puede ser múltiple. Este concepto se detalla más adelante.
Cuando escribimos en el intérprete:
>>> class Vacia:
... pass
...
Es equivalente a escribir:
>>> class Vacia(object):
... pass
...
Sin especificar, por defecto, la clase hereda de la clase object.
Terminología POO: la clase Vacia hereda de la clase object. También se dice que la clase Vacía es una clase...
Ejemplos de scripts
Es hora de poner todo esto en práctica.
Este diagrama ilustra lo que haremos en un ejemplo:
A partir de una clase Document genérica y gracias al concepto de herencia y sobrecarga, crearemos otras clases con funcionamientos específicos.
Escenario:
Tras la implantación de un nuevo sistema de control de calidad, se nos pide generar semanalmente el resultado de las copias de seguridad de los tres entornos PRODUCCIÓN, EVOLUTIVO, DEV_TEST en forma de documento en papel, para guardar en un archivador.
Ejemplo del informe
RESULTADO DE LAS COPIAS DE SEGURIDAD
------------------------
Fecha PROD EVOLUTIVO DEV & TEST
----------------------------------------------------
Lunes OK OK OK
Martes OK OK OK
Miércoles OK OK OK
Jueves OK OK OK
Viernes OK OK OK
Documento creado el <fecha> por <autor>
Analicemos la solicitud.
Esto implica producir un documento que contenga la siguiente información:
-
Un título
-
Un contenido variable
-
Un pie de página con la fecha y el autor.
Y este documento, queremos imprimirlo.
Resultado del análisis:
-
=> El objeto: un documento
-
=> Sus atributos: título, contenido, fecha y hora, y autor
-
=> Un método: print
Esto nos permite ilustrar la "forma de pensar en objetos". No vamos a detenernos en el tipo preciso de los datos que estamos manejando, sino mucho más en el modelado de los objetos, partiendo de la necesidad y las interacciones de los objetos.
Un análisis de objetos debe mantenerse alejado de cualquier lenguaje específico.
También se ha creado para este propósito una notación llamada UML, para Unified Modeling Language en inglés.
Por ejemplo, es posible...
Resumen
Este capítulo le ha presentado los conceptos básicos de POO en lenguaje Python.
Python ofrece tipos básicos potentes y flexibles, que le permiten construir piezas de software, manteniendo la legibilidad y simplicidad de este lenguaje.
La POO le aporta la herencia, que le permite reutilizar clases para crear otras, más especializadas y adaptadas a las necesidades.
Aunque si "objeto pensante" puede parecer un poco confuso, debería empezar a ver las posibilidades que ofrece la POO.
Sin embargo, aún quedan algunos conceptos más avanzados por descubrir, pero también algunos conceptos básicos por profundizar.
Además, Python viene con una enorme librería de módulos que es necesario conocer, para no reinventar la rueda con cada script; es precisamente el tema del siguiente capítulo.