Mini tutorial de OO con Python

Python

Esta receta es una pequeña introducción al soporte de Python para diseño orientado a objetos. Para programadores noveles, se aconseja primero la lectura de nuestra ya veterana receta Mini tutorial de Python.

Introducción

Se dice que Python es un lenguaje multi-paradigma ya que incorpora facilidades para enfoques muy distintos a la hora de implementar un diseño. En esta ocasión voy a comentar el soporte de Python para el veterano paradigma de la “orientación a objetos”. Para el programador Python novel puede resultar chocante algunas de las cosas que permite Python, por ejemplo, no hay mecanismos de protección en las clases.

Los diseñadores de Python consideran que no es posible hacer lenguajes a prueba de tontos (aunque los de Java parece que sí), de modo que muchos mecanismos de programación “defensiva” incorporados en los lenguajes pueden ser complejos de implementar y en la práctica inútiles. La solución es programar bien, el lenguaje no puede (y según muchos, no debe) suplir las deficiencias del programador.

Requisitos

La clase

La elemento básico de todo programa orientado a objeto es la clase, que no deja de ser más que un tipo de datos definido por el usuario. con la particularidad de que a parte de establecer el formato de un conjunto de datos relacionado, define qué acciones se le pueden aplicar. La clase más sencilla que se puede escribir en Python es algo como:

class A: pass

que es una clase vacía, pero que puede ser útil en algunas ocasiones.

Métodos

Un método es un conjunto de sentencias con un nombre asociado que se aplican sobre una instancia de la clase, es decir, es una función ligada a la clase que determina algo que los objetos de esa clase son capaces de hacer o de recibir. El programador puede definir un buen número de métodos especiales en una clase Python y el más común de todos ellos es el constructor. El nombre de todos los métodos especiales empieza y acaba con un doble guión bajo “__” . He aquí una clase muy simple con un constructor:

class A:
    def init(self):
        print "hello OO world"

En realidad el método __init__() no construye la instancia, lo que hace (como su nombre indica) es inicializar la instancia. Lo habitual es usar este método para dar valores iniciales correctos a los atributos de la instancia, quizá a partir de los parámetros. También se debe preocupar de que el objeto resultante es correcto, elevando alguna excepción de no ser posible.

Para definir un objeto de clase A, simplemente escribe:

>>> a = A()
hello OO world

self

Quizá te habrás percatado de que el constructor tiene un argumento llamado self pero en el código anterior, en el que se crea el objeto, no se pasa nada como parámetro. Bueno, pues ese self es un parámetro un tanto especial; representa a la instancia sobre la que opera el método. En realidad los métodos son funciones normales que reciben como primer parámetro la dirección del objeto sobre el que deben trabajar. Esto es así en la mayoría de los lenguajes orientados a objetos, sólo que normalmente ese hecho se oculta al programador. Por ejemplo, basta con usar un depurador en un programa C++ para ver un curioso puntero llamado this como primer parámetro de cada método.

De modo que Python no es tan distinto, la diferencia es que Python no lo oculta, lo muestra, es explícito. Todo es explícito, es parte de la filosofía de diseño de Python.

Principios de la orientación a objetos

Un lenguaje orientado a objetos debería tener al menos las siguientes características:

  • Encapsulación
  • Ocultación
  • Polimofismo
  • Herencia

Veamos cómo las maneja Python.

Encapsulación

Los detalles de cada tipo no son visibles desde fuera de la clase. Cada clase contiene atributos y métodos propios y representa un concepto del mundo real maximizando la cohesión.

Python ofrece las herramientas habituales de todo lenguaje orientado a objetos aunque bien es cierto que por ser un lenguaje dinámico puede sorprender a programador acostumbrados a lenguajes compilados. Por ejemplo, en Python es factible (y sencillo) añadir o eliminar atributos y métodos en cualquier momento de la vida del objeto. Es un mecanismo potente y como tal debe usarse con cuidado dado que en malas manos puede hacer programas poco mantenibles.

Ocultación

Cada objeto dispone de una interfaz bien especificado para poder solicitar sus servicios, normalmente por el envío de mensajes (invocación de métodos). Esto implica que las partes internas del objeto no son accesibles ni modificables desde fuera.

Esta principio está muy relajado en Python. Al contrario que lenguajes como C++, Java o C#, en Python no existen los típicos mecanismos de control de acceso public, protected y private. Se asume por diseño que el programador es responsable de usar la clase del modo adecuado. Uno de los pocos mecanismos de protección consiste en anteponer un doble guión bajo (”__”) al nombre de los atributos y métodos que no puedan ser invocados desde fuera de la clase, y tampoco se trata de una imposición férrea.

Herencia

Es el mecanismo que permite crear jerarquías de clases, de modo que es posible crear clases nuevas a partir de otras existentes por medio de la especialización (añadiendo o modificando comportamiento).

Python permite herencia múltiple. Como en casos anteriores, este es un mecanismo muy potente que puede dar muchos problemas (hasta el punto que lenguajes más conservadores como Java no lo permiten, aún). Sin embargo, resulta muy útil en muchas situaciones, por ejemplo para implementar clases mix-in.

Polimofismo

Permite que cada clase pueda especificar el comportamiento de un método común (heredado) de modo que el método hará algo distinto dependiendo de la clase a la que corresponda el objeto.

Python ofrece este mecanismo aunque no es posible definir varias funciones con el mismo nombre y distintos argumentos (como ocurre en otros lenguajes). Sí que permite realizar sobrecarga de operadores, algo muy valioso para conseguir programas más legibles y mantenibles y conseguir tipos que se compartan exactamente igual que los que incorpora el lenguaje.

[en construcción]