5.3. Definición de clases

Python está completamente orientado a objetos: puede definir sus propias clases, heredar de las que usted defina o de las incorporadas en el lenguaje, e instanciar las clases que haya definido.

Definir una clase en Python es simple. Como con las funciones, no hay una definición interfaz separada. Simplemente defina la clase y empiece a escribir código. Una clase de Python empieza con la palabra reservada class, seguida de su nombre. Técnicamente, esto es todo lo que necesita, ya que la clase no tiene por qué heredar de ninguna otra.

Ejemplo 5.3. La clase más simple en Python


class Loaf: 1
    pass    2 3
1 El nombre de esta clase es Loaf, y no hereda de ninguna otra. Los nombres de clases suelen comenzar en mayúscula, CadaPalabraAsí, pero esto es sólo una convención, no un requisito.
2 Esta clase no define métodos ni atributos, pero sintácticamente, hace falta que exista alguna cosa en la definición, de manera que usamos pass. Ésta es una palabra reservada de Python que simplemente significa “múevanse, nada que ver aquí”. Es una sentencia que no hace nada, y es un buen sustituto de funciones o clases modelo, que no hacen nada.
3 Probablemente haya adivinado esto, pero todo dentro de una clase va sangrado, igual que el código dentro de una función, sentencia if, bucle for, etc.. La primera cosa que no esté sangrada, no pertenece a la clase.
nota
La sentencia pass de Python es como unas llaves vacías ({}) en Java o C.

Por supuesto, siendo realistas, la mayoría de las clases heredarán de alguna otra, y definirán sus propios métodos y atributos. Pero como acabamos de ver, no hay nada que una clase deba tener, aparte de su nombre. En particular, los programadores de C++ encontrarán extraño que las clases de Python no tengan constructores o destructores explícitos. Las clases de Python tienen algo similar a un constructor: el método __init__.

Ejemplo 5.4. Definición de la clase FileInfo


from UserDict import UserDict

class FileInfo(UserDict): 1
1 En Python, el ancestro de una clase se lista simplemente entre paréntesis inmediatamente del nombre de la clase. Así que la clase FileInfo hereda de la clase UserDict (que importamos del módulo UserDict). UserDict es una clase que actúa como un diccionario, permitiéndole derivar el tipo de datos diccionario y añadir comportamientos a su gusto (existen las clases similares UserList y UserString que le permiten derivar listas y cadenas). Hay un poco de magia negra tras todo esto, que demistificaré más adelante en este capítulo cuando exploremos la clase UserDict en más profundidad.
nota
En Python, el ancestro de una clase se lista entre paréntesis inmediatamente tras el nombre de la clase. No hay palabras reservadas especiales como extends en Java.

Python admite herencia múltiple. En los paréntesis que siguen al nombre de la clase, puede enumerar tantas clases ancestro como desee, separadas por comas.

5.3.1. Inicialización y programación de clases

Este ejemplo muestra la inicialización de la clase FileInfo usando el método __init__.

Ejemplo 5.5. Inicialización de la clase FileInfo


class FileInfo(UserDict):
    "store file metadata"              1
    def __init__(self, filename=None): 2 3 4
1 Las clases pueden también (y deberían) tener cadenas de documentación, al igual que los módulos y funciones.
2 __init__ se llama inmediatamente tras crear una instancia de la clase. Sería tentador pero incorrecto denominar a esto el constructor de la clase. Es tentador porque parece igual a un constructor (por convención, __init__ es el primer método definido para la clase), actúa como uno (es el primer pedazo de código que se ejecuta en una instancia de la clase recién creada), e incluso suena como una (“init” ciertamente sugiere una naturaleza constructórica). Incorrecto, porque el objeto ya ha sido construido para cuando se llama a __init__, y ya tiene una referencia válida a la nueva instancia de la clase. Pero __init__ es lo más parecido a un constructor que va a encotnrar en Python, y cumple el mismo papel.
3 El primer método de cada método de clase, incluido __init__, es siempre una referencia a la instancia actual de la clase. Por convención, este argumento siempre se denomina self. En el método __init__, self se refiere al objeto recién creado; en otros métodos de la clase, se refiere a la instancia cuyo método ha sido llamado. Aunque necesita especificar self de forma explícita cuando define el método, no se especifica al invocar el método; Python lo añadirá de forma automática.
4 Los métodos __init__ pueden tomar cualquier cantidad de argumentos, e igual que las funciones, éstos pueden definirse con valores por defecto, haciéndoles opcionales para quien invoca. En este caso, filename tiene el valor por omisión de None, que es el valor nulo de Python.
nota
Por convención, el primer argumento de cualquier clase de Python (la referencia a la instancia) se denomina self. Este argumento cumple el papel de la palabra reservada this en C++ o Java, pero self no es una palabra reservada en Python, sino una mera convención. De todas maneras, por favor no use otro nombre sino self; es una convención muy extendida.

Ejemplo 5.6. Programar la clase FileInfo


class FileInfo(UserDict):
    "store file metadata"
    def __init__(self, filename=None):
        UserDict.__init__(self)        1
        self["name"] = filename        2
                                       3
1 Algunos lenguajes pseudo-orientados a objeto como Powerbuilder tienen un concepto de “extender” constructores y otros eventos, para los que el método ancestro se llama de forma automática antes de que se ejecute el método del descendiente. Python no hace esto; siempre ha de llamar de forma explícita a los métodos de los ancestros.
2 Le dije antes que esta clase actúa como un diccionario, y aquí está la primera señal de ello. Está asignando el argumento filename como valor de la clave name de este objeto.
3 Advierta que el método __init__ nunca devuelve un valor.

5.3.2. Saber cuándo usar self e __init__

Cuando defina los métodos de su clase, debe enumerar self de forma explícita como el primer argumento de cada método, incluido __init__. Cuando llame a un método de una clase ancestra desde dentro de la clase, debe incluir el argumento self. Pero cuando invoque al método de su clase desde fuera, no debe especificar nada para el argumento self; evítelo completamente, y Python añadirá automáticamente la referencia a la instancia. Soy consciente de que esto es confuso al principio; no es realmente inconsistente, pero puede parecer inconsistente debido a que se basa en una distinción (entre métodos bound y unbound) que aún no conoce.

¡Vaya! Me doy cuenta de que es mucho por absorber, pero le acabará pillando el truco. Todas las clases de Python funcionan de la misma manera, de manera que cuando aprenda una, las habrá aprendido todas. Si se olvida de algo, recuerde esto, porque prometo que se lo tropezará:

nota
Los métodos __init__ son opcionales, pero cuando define uno, debe recordar llamar explícitamente al método __init__ del ancestro (si define uno). Suele ocurrir que siempre que un descendiente quiera extender el comportamiento de un ancestro, el método descendiente deba llamar al del ancestro en el momento adecuado, con los argumentos adecuados.