Subsecciones


9. Clases

El mecanismo de clases de Python a�ade clases al lenguaje con un m�nimo de sintaxis y sem�ntica nueva. Es una mezcla de los mecanismos de clase de C++ y Modula-3. Como en los m�dulos, las clases de Python no ponen una barrera absoluta entre la definici�n y el usuario, sino que m�s bien se f�an de la buena educaci�n del usuario para no ``invadir la definici�n''. Se mantienen las caracter�sticas m�s importantes con plena potencia. El mecanismo de herencia de clases permite m�ltiples clases base, una clase derivada puede redefinir cualquier m�todo de sus clases base y un m�todo puede llamar a un m�todo de una clase base con el mismo nombre. Los objetos pueden contener una cantidad arbitraria de datos privados.

En terminolog�a C++, todos los miembros de la clase (datos incluidos) son p�blicos y todas las funciones miembro son virtuales. No hay constructores ni destructores especiales. Como en Modula-3, no existen abreviaturas para hacer referencia a los miembros del objeto desde sus propios m�todos. La funci�n m�todo se declara con un primer argumento expl�cito que representa al objeto y que se proporciona impl�citamente al llamar a la funci�n. Como en Smalltalk, las clases son ellas mismas objetos, aunque en un sentido m�s amplio de la palabra: en Python, todos los tipos de datos son objetos. Esto proporciona la sem�ntica para importar y renombrar. Sin embargo, como en C++ o en Modula3, los tipos internos no pueden ser la clase base para extensiones del usuario. Adem�s, como en C++ y al contrario de Modula-3, la mayor�a de operadores internos con sintaxis especial (operadores aritm�ticos, �ndices, etc.) se pueden redefinir en las clases.


9.1 Unas palabras sobre la terminolog�a

A falta de una terminolog�a universalmente aceptada para hablar de clases, har� uso ocasional de t�rminos de Smalltalk y C++ (har�a uso de t�rminos de Modula-3, ya que la sem�ntica orientada al objeto es m�s cercana a Python que la de C++, pero no espero que muchos lectores la dominen).

Tambi�n he notado que hay un agujero en la terminolog�a de los lectores orientados a objetos: La palabra ``objeto'' en Python no significa necesariamente una instancia de una clase. Como en C++ y en Modula-3, al contrario que en Smalltalk, no todos los tipos son clases: Los tipos internos, como listas y enteros no lo son, ni siquiera algunos tipos m�s ex�ticos, como los ficheros. Sin embargo, todos los tipos de Python comparten algo de sem�ntica com�n, descrita adecuadamente mediante la palabra ``objeto''.

Los objetos tienen individualidad. Se pueden asociar m�ltiples nombres (y en diferentes �mbitos) al mismo objeto, lo que se conoce como ``generar alias'' en otros lenguajes. Esto no se aprecia a primera vista en Python y no hace falta tenerlo en cuenta cuando se trata con tipos inmutables (n�meros, cadenas, tuplas...). Sin embargo los alias tienen un efecto (�buscado!) en la sem�ntica del c�digo Python que involucra los objetos mutables, como listas, diccionarios y la mayor�a de los tipos que representan entidades externas al programa (ficheros, ventanas...). Se suele usar en beneficio del programa, ya que los alias se comportan como punteros en algunos aspectos. Por ejemplo, pasar un objeto es poco costoso, ya que la implementaci�n s�lo pasa un puntero. Si la funci�n modifica el objeto que pasa como argumento, el que llama a la funci�n ver� los cambios. De este modo se elimina la necesidad de tener los dos mecanismos de traspaso de argumentos de Pascal.


9.2 �mbitos y espacios nominales en Python

Antes de presentar las clases, debo contar algo sobre las reglas de alcance de Python. Las definiciones de clases realizan truquitos con los espacios nominales y se necesita saber c�mo funcionan los alcances y espacios nominales para comprender plenamente lo que ocurre. Incidentalmente, el conocimiento de este tema es �til para cualquier programador en Python avanzado.

Empecemos con unas definiciones.

Un espacio nominal es una correspondencia entre nombres y objetos. La mayor�a de los espacios de nombres se implementan en la actualidad como diccionarios, pero eso no se nota en modo alguno (salvo por el rendimiento) y puede cambiar en el futuro. Ejemplos de espacios nominales son:

En cierto sentido, el conjunto de atributos de un objeto tambi�n forma un espacio nominal. Lo que hay que tener claro de los espacios nominales es que no existe absolutamente ninguna relaci�n entre los nombres de diferentes espacios. Por ejemplo, dos m�dulos pueden definir una funci�n ``maximizar'' sin posibilidad de confusi�n; los usuarios de los m�dulos deben preceder el nombre con el nombre del m�dulo.

Por cierto, utilizo la palabra atributo para referirme a cualquier nombre que venga detr�s de un punto; por ejemplo, en la expresi�n z.real, real es un atributo del objeto z. Hablando estrictamente, las referencias a nombres en un m�dulo son referencias a atributos. En la expresi�n nombremod.nombrefunc, nombremod es un objeto m�dulo y nombrefunc es atributo suyo. En este caso, resulta que existe una correspondencia directa entre los atributos del m�dulo y los nombres globales definidos en el m�dulo: �comparten el mismo espacio nominal9.1!

Los atributos pueden ser de s�lo lectura o de lectura/escritura. En este caso, es posible asignar valores a los atributos. Los atributos de los m�dulos son de lectura/escritura: Se puede escribir "nombremod.respuesta = 42". Los atributos de lectura/escritura se pueden borrar con la sentencia del, por ejemplo: "del nombremod.respuesta".

Los espacios nominales se crean en diferentes momentos y tienen tiempos de vida diferentes. El espacio nominal que contiene los nombres internos se crea al arrancar el int�rprete de Python y nunca se borra. El espacio nominal global de un m�dulo se crea al leer la definici�n del m�dulo. Normalmente, los espacios nominales de los m�dulos tambi�n duran hasta que se sale del int�rprete. Las sentencias ejecutadas por el nivel superior de llamadas, le�das desde un guion o interactivamente, se consideran parte de un m�dulo denominado __main__, as� que tienen su propio espacio nominal (los nombres internos tambi�n residen en un m�dulo, llamado __builtin__).

El espacio nominal local de una funci�n se crea cuando se llama a la funci�n y se borra al salir de la funci�n, por una sentencia "return"o si salta una excepci�n que la funci�n no captura (en realidad, lo m�s parecido a lo que ocurre es el olvido). Por supuesto, las llamadas recursivas generan cada una su propio espacio nominal.

Un �mbito es una regi�n textual de un programa Python en que el espacio nominal es directamente accesible. ``Directamente accesible'' significa en este contexto una referencia sin calificar (sin puntos) que intenta encontrar un nombre dentro de un espacio nominal.

Aunque los �mbitos se determinan est�ticamente, se utilizan din�micamente. En cualquier punto de la ejecuci�n, existen exactamente tres �mbitos anidados (es decir, hay tres espacios nominales accesibles directamente): el �mbito interior, el primero en que se busca el nombre, que contiene los nombres locales, el �mbito medio, siguiente en la b�squeda de nombres, que contiene los nombres globales del m�dulo, y el �mbito externo, el �ltimo en la b�squeda, que contiene los nombres internos.

Normalmente, el �mbito local hace referencia a los nombres locales de la funci�n en curso (textualmente). Fuera de las funciones, el �mbito local hace referencia al mismo espacio nominal que el �mbito global: el espacio nominal del m�dulo. Las definiciones de clases colocan otro espacio nominal en el �mbito local.

Es importante darse cuenta de que los �mbitos se determinan textualmente: El �mbito global de una funci�n definida en un m�dulo es el espacio nominal de este m�dulo, sin importar desde d�nde o con qu� alias se haya llamado a la funci�n. Por otra parte, la b�squeda real de nombres se lleva a cabo din�micamente, en tiempo de ejecuci�n. Sin embargo, la definici�n del lenguaje tiende a la resoluci�n est�tica de los nombres, as� que �no te f�es de la resoluci�n din�mica de los nombres! De hecho ya se determinan est�ticamente las variables locales.

Un asunto especial de Python es que las asignaciones siempre van al �mbito m�s interno. Las asignaciones no copian datos, simplemente enlazan nombres a objetos. Lo mismo vale para los borrados: la sentencia "del x" elimina el enlace de x del espacio nominal al que hace referencia el �mbito local. De hecho, todas las operaciones que introducen nombres nuevos utilizan el �mbito local. Particularmente, las sentencias import y las definiciones de funciones asocian el nombre del m�dulo o funci�n al �mbito local. Se puede utilizar la sentencia global para indicar que ciertas variables residen en el �mbito global.


9.3 Un primer vistazo a las clases

Las clases introducen una pizca de sintaxis nueva, tres tipos de objeto nuevos y algo de sem�ntica nueva.


9.3.1 Sintaxis de definici�n de clases

La forma m�s simple de definici�n de clase tiene este aspecto:

class nombreClase:
    <sentencia-1>
    .
    .
    .
    <sentencia-N>

Las definiciones de clases, como las definiciones de funciones (sentencias def) deben ejecutarse para tener efecto (es perfectamente correcto colocar una definici�n de clase en una rama de una sentencia if o dentro de una funci�n).

En la pr�ctica, las sentencias de dentro de una definici�n de clase ser�n definiciones de funciones, pero se permite otro tipo de sentencias, lo que resulta �til en algunos casos, ya veremos esto. Las definiciones de funciones interiores a la clase suelen tener una lista de argumentos un poco especial, dictada por las convenciones de llamada a m�todo. Esto tambi�n se explica m�s adelante.

Cuando se entra en una definici�n de clase, se genera un nuevo espacio nominal, que se utiliza como �mbito local; as� que todas las asignaciones a variables locales caen dentro de este nuevo espacio nominal. En particular, las definiciones de funciones enlazan aqu� el nombre de la nueva funci�n.

Cuando se abandona una definici�n de clase de manera normal (se ejecuta la �ltima l�nea de su c�digo), se crea un objeto de clase. Es, sencillamente, un envoltorio del contenido del espacio nominal creado por la definici�n de la clase. Se ver� con m�s detalle en la siguiente secci�n. El �mbito local original (el que estaba activo cuando se entr� en la definici�n de clase) se reinstancia y el objeto clase se enlaza con el nombre de clase dado en la cabecera de la funci�n (en el ejemplo nombreClase).


9.3.2 Objetos clase

Los objetos de clase soportan dos tipos de operaciones: referencia a atributos e instanciaci�n.

Las referencias a atributos utilizan la sintaxis est�ndar que se utiliza para todas las referencias a atributos en Python: obj.nombre. Los nombres de atributos v�lidos son todos los nombres que estaban en el espacio nominal de la clase cuando fue creada la clase. Por lo tanto, si la definici�n de la clase tiene este aspecto:

class MiClase:
    "Simple clase de ejemplo"
    i = 12345
    def f(x):
        return 'hola, mundo'

MiClase.i y MiClase.f son referencias a atributos v�lidas, que devuelven un entero y un objeto m�todo, respectivamente. Tambi�n se puede asignar valores a los atributos de una clase; puedes cambiar el valor de MiClase.i con una asignaci�n. __doc__ es tambi�n un atributo v�lido, que devuelve la cadena de documentaci�n que corresponde a la clase: "Simple clase de ejemplo".

La instanciaci�n de clases utiliza notaci�n de funci�n. Basta con imaginar que el objeto clase es una funci�n sin par�metros que devuelve una instancia nueva de la clase. Por ejemplo, siguiendo con el ejemplo anterior:

x = MiClase()

crea una nueva instancia de la clase y la asigna a la variable local x. La operaci�n de instanciaci�n (``la llamada'' a un objeto clase) genera un objeto vac�o. Muchas clases prefieren generar los objetos en un estado inicial conocido. Por ello, una clase puede definir un m�todo especial denominado __init__(), as�:

    def __init__(self):
        self.vaciar()

Cuando una clase define un m�todo __init__(), la instanciaci�n de clases llama autom�ticamente a __init__() para la instancia de clase reci�n creada. As� que, en el ejemplo de la Bolsa, se puede obtener una instancia de clase inicializada nueva mediante:

x = Bolsa()

Por supuesto, el m�todo __init__() podr�a tener argumentos que le a�adir�an flexibilidad. En tal caso, los argumentos proporcionados al operador de instanciaci�n de clase se pasan a __init__(). Por ejemplo,

>>> class Complejo:
...     def __init__(self, parteReal, parteImaginaria):
...         self.r = parteReal
...         self.i = parteImaginaria
... 
>>> x = Complejo(3.0,-4.5)
>>> x.r, x.i
(3.0, -4.5)


9.3.3 Objetos instancia

�Qu� se puede hacer con los objetos instancia? Las �nicas operaciones que entienden son las referencias a atributos. Hay dos tipos de nombres de atributo v�lidos.

A los primeros los voy a llamar atributos de datos. Corresponden a las ``variables de instancia'' de Smalltalk o a los ``miembros dato'' de C++. No es necesario declarar los atributos de datos. Como las variables locales, aparecen por arte de magia la primera vez que se les asigna un valor. Por ejemplo, si x es una instancia de la clase MiClase creada anteriormente, el c�digo siguiente mostrar� el valor 16 sin dejar rastro:

x.contador = 1
while x.contador < 10:
    x.contador = x.contador * 2
print x.contador
del x.contador

El segundo tipo de referencia a atributo que entienden los objetos instancia son los m�todos. Un m�todo es una funci�n que ``pertenece a'' un objeto. En Python, el t�rmino m�todo no se limita a las instancias de una clase, ya que otros tipos de objeto pueden tener m�todos tambi�n. Por ejemplo, los objetos de tipo lista tienen m�todos llamados append, insert, remove, sort, etc. Sin embargo, vamos a utilizar ahora el t�rmino exclusivamente para referirnos a los m�todos de objetos instancia de una clase, salvo que se indique lo contrario.

Los nombres v�lidos de m�todos de un objeto instancia dependen de su clase. Por definici�n, todos los atributos de una clase que son objetos funci�n (definidos por el usuario) definen los m�todos correspondientes de sus instancias. As� que, en nuestro ejemplo, x.f es una referencia a m�todo correcta, ya que MiClase.f es una funci�n, pero x.i no lo es, ya que MiClase.i no es una funci�n. Pero x.f no es lo mismo que MiClase.f - es un objeto m�todo, no un objeto funci�n.


9.3.4 Objetos m�todo

Normalmente, se llama a un m�todo de manera inmediata, por ejemplo:

x.f()

En nuestro ejemplo, esto devuelve la cadena 'hola, mundo'. Sin embargo, no es necesario llamar a un m�todo inmediatamente: x.f es un objeto m�todo y se puede almacenar y recuperar m�s tarde, por ejemplo:

xf = x.f
while 1:
    print xf()

mostrar� "hola, mundo" hasta que las ranas cr�en pelo.

�Qu� ocurre exactamente cuando se llama a un m�todo? Habr�s observado que x.f() fue invocado sin argumento, aunque la definici�n del m�todo f especificaba un argumento. �Qu� le ha pasado al argumento? Desde luego, Python hace saltar una excepci�n cuando se llama a una funci�n que necesita un argumento sin especificar ninguno (aunque no se utilice)...

En realidad, te puedes imaginar la respuesta: Lo que tienen de especial los m�todos es que el objeto que los llama se pasa como primer argumento de la funci�n. En nuestro ejemplo, la llamada x.f() es totalmente equivalente a MiClase.f(x). En general, llamar a un m�todo con una lista de argumentos es equivalente a llamar a la funci�n correspondiente con la lista de argumentos resultante de insertar el objeto del m�todo al principio de la lista de argumentos original.

Si todav�a no entiendes c�mo funcionan los m�todos, igual te aclara las cosas un vistazo a la implementaci�n. Cuando se hace referencia a un atributo de una instancia que no es un atributo de datos, se busca en su clase. Si el nombre denota un atributo de clase v�lido que resulta ser un objeto funci�n, se crea un objeto m�todo empaquetando juntos (punteros hacia) el objeto instancia y el objeto funci�n reci�n encontrado en un objeto abstracto: el objeto m�todo. Cuando se llama al objeto m�todo con una lista de argumentos, se desempaqueta de nuevo, se construye una nueva lista de argumentos a partir del objeto instancia y la lista de argumentos original y se llama al objeto funci�n con la nueva lista de argumentos.


9.4 Caj�n de sastre

[Igual habr�a que colocar esto con m�s cuidado...]

Los atributos de datos se tienen en cuenta en lugar de los atributos m�todo con el mismo nombre. Para evitar conflictos nominales accidentales, que podr�an causar errores dif�ciles de rastrear en programas grandes, conviene utilizar alg�n tipo de convenci�n que minimice la probabilidad de conflictos, por ejemplo, poner en may�sculas los nombres de m�todos, preceder los nombre de atributos de datos con una peque�a cadena �nica (o s�lo un guion bajo) o usar verbos para los m�todos y nombres para los atributos de datos.

Los m�todos pueden hacer referencia a atributos de datos tanto como los usuarios normales (los clientes) de un objeto. En otras palabras, no es posible usar las clases para implementar tipos de datos abstractos puros. De hecho, no hay nada en Python para posibilitar la ocultaci�n de datos, todo se basa en convenciones (por otra parte, la implementaci�n de Python escrita en C puede ocultar completamente los detalles de implementaci�n y el control de acceso a un objeto si es necesario, lo que pueden hacer tambi�n las extensiones a Python escritas en C).

Los clientes deben utilizar los atributos de datos con cuidado. Los clientes pueden embrollar invariantes mantenidas por los m�todos, chaf�ndolas con sus atributos de datos. Observa que los clientes pueden a�adir atributos de datos propios a una instancia de objeto sin afectar a la validez de los m�todos, siempre que se eviten los conflictos de nombres. Nuevamente, ser coherente en los nombres puede ahorrarnos un mont�n de dolores de cabeza.

No hay un atajo para hacer referencia a los atributos dato (�ni a otros m�todos!) desde los m�todos. Encuentro que esto, en realidad, favorece la legibilidad de los m�todos, porque as� no hay manera de confundir las variables locales y las variables de la instancia cuando se repasa un m�todo.

Por convenci�n, el primer argumento de los m�todos se suele llamar self (yo mismo). No es m�s que una convenci�n, el nombre self no le dice nada a Python (sin embargo, no seguir esta convenci�n hace que tu c�digo sea menos legible por otros programadores y no ser�a extra�o que haya navegadores de la jerarqu�a de clases que suponen que la sigues).

Cualquier objeto funci�n que es atributo de una clase define un m�todo para las instancias de esa clase. No es necesario que la definici�n de la funci�n est� textualmente encerrada en la definici�n de la clase. Asignar un objeto funci�n a una variable local de la clase tambi�n vale. Por ejemplo:

# Funci�n definida fuera de la clase
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1
    def g(self):
        return 'hola, mundo'
    h = g

Ahora f, g y h son atributos de la clase C que hacen referencia a objetos funci�n, por lo que los tres son m�todos de las instancias de la clase C, siendo h exactamente equivalente a g. Observa que esta pr�ctica suele valer s�lo para confundir al lector del programa.

Los m�todos pueden llamar a otros m�todos utilizando los atributos m�todo del argumento self, por ejemplo:

class Bolsa:
    def __init__(self):
        self.datos = []
    def agregar(self, x):
        self.datos.append(x)
    def agregarDosVeces(self, x):
        self.add(x)
        self.add(x)

Los m�todos puede hacer referencia a los nombres globales del mismo modo que las funciones normales. El �mbito global asociado a un m�todo en un m�todo es el m�dulo que contiene la definici�n de la clase (la clase en s� nunca se utiliza como �mbito global). Aunque es raro encontrar un buen motivo para usar un dato global en un m�todo, hay bastantes usos leg�timos del �mbito global: de momento, los m�todos pueden usar las funciones y los m�dulos importados al �mbito global, al igual que las funciones y las clases definidas en �l. Normalmente, la clase que contiene el m�todo est� definida en este �mbito global. En la siguiente secci�n encontraremos algunas buenas razones para que un m�todo haga referencia a su propia clase.


9.5 Herencia

Por supuesto, una caracter�stica de un lenguaje no ser�a digna del nombre ``clase'' si no aportara herencia. La sintaxis de una definici�n de clase derivada tiene este aspecto:

class nombreClaseDerivada(nombreClaseBase):
    <sentencia-1>
    .
    .
    .
    <sentencia-N>

El nombre nombreClaseBase debe estar definido en un �mbito que contenga la definici�n de la clase derivada. En lugar de un nombre de clase base, se permite poner una expresi�n. Esto es �til cuando la clase base est� definida en otro m�dulo, por ejemplo,

class nombreClaseDerivada(nombreMod.nombreClaseBase):

La ejecuci�n de la definici�n de una clase derivada se lleva a cabo del mismo modo que la clase base. Cuando se construye el objeto de la clase, se recuerda la clase base. Esto se utiliza para resolver referencias a atributos: si no se encuentra un atributo solicitado en la clase, se busca en la clase base. Esta regla se aplica recursivamente si la clase base es a su vez derivada.

No hay nada especial sobre la instanciaci�n de las clases derivadas: nombreClaseDerivada() crea una nueva instancia de la clase. Las referencias a m�todos se resuelven de la siguiente manera: Se busca el atributo de la clase correspondiente, descendiendo por la cadena de clases base, si es necesario, y la referencia a m�todo es correcta si de este modo se obtiene un objeto funci�n.

Las clases derivadas pueden redefinir m�todos de sus clases base. Como los m�todos no tienen privilegios especiales al llamar a otros m�todos del mismo objeto, un m�todo de una clase base que llama a otro m�todo definido en la misma clase base puede acabar llamando a un m�todo de una clase derivada que lo redefina (para los programadores de C++: todos los m�todos en Python son virtuales).

Puede que una redefinici�n de m�todo en una clase derivada quiera ampliar, en lugar de reemplazar, el m�todo de la clase base del mismo nombre. Existe un modo sencillo de llamar al m�todo de la clase base directamente: simplemente, utilizar "nombreClaseBase.nombreM�todo(self, argumentos)". Esto tambi�n les vale a los clientes, en ciertas ocasiones (observa que esto s�lo funciona si la clase base est� definida o se ha importado directamente en el �mbito global).


9.5.1 Herencia m�ltiple

Python tambi�n aporta una forma limitada de herencia m�ltiple. Una definici�n de clase con m�ltiples clases base tiene este aspecto:

class nombreClaseDerivada(Base1, Base2, Base3):
    <sentencia-1>
    .
    .
    .
    <sentencia-N>

La �nica regla necesaria para explicar la sem�ntica es la regla de resoluci�n utilizada para las referencias a atributos de la clase. Se busca primero por profundidad y luego de izquierda a derecha. De este modo, si no se encuentra un atributo en nombreClaseDerivada, se busca en Base1, en las clases base de Base1 y, si no se encuentra, en Base2, en las clases base de �sta y as� sucesivamente.

Para algunos parece m�s natural buscar en Base2 y en Base3 antes que en las clases base de Base1. Sin embargo, esto exigir�a conocer si un atributo particular de Base1 est� definido realmente en Base1 en una de sus clases base, antes de imaginarse las consecuencias de un conflicto de nombres con un atributo de la Base2. La regla de buscar primero por profundidad no diferencia entre atributos de la Base1 directos y heredados.

Queda claro que el uso indiscriminado de la herencia m�ltiple hace del mantenimiento una pesadilla, dada la confianza de Python en las convenciones para evitar los conflictos de nombre accidentales. Un problema bien conocido de la herencia m�ltiple es el de una clase derivada de dos clases que resulta que tienen una clase base com�n. A pesar de que resulta sencillo, en este caso, figurarse qu� ocurre (la instancia tendr� un sola copia de las ``variables de la instancia'' o atributos de datos utilizada por la base com�n), no queda clara la utilidad de este tipo de pr�cticas.


9.6 Variables privadas

Se pueden utilizar, de manera limitada, identificadores privados de la clase. Cualquier identificador de la forma __fiambre (al menos dos guiones bajos iniciales, no m�s de un guion bajo final) se reemplaza textualmente ahora con _nombreClase__fiambre, donde nombreClase es la clase en curso, eliminando los guiones bajos iniciales. Esta reescritura se realiza sin tener en cuenta la posici�n sint�ctica del identificador, por lo que se puede utilizar para definir, de manera privada, variables de clase e instancia, m�todos y variables globales. Tambi�n sirve para almacenar variables de instancia privadas de esta clase en instancias de otras clases. Puede que se recorten los nombres cuando el nombre reescrito tendr�a m�s de 255 caracteres. Fuera de las clases o cuando el nombre de la clase consta s�lo de guiones bajos, no se reescriben los nombres.

La reescritura de nombres pretende dar a las clases un modo sencillo de definir m�todos y variables de instancia ``privados'', sin tener que preocuparse por las variables de instancia definidas por las clases derivadas ni guarrear con las variables de instancia por el c�digo externo a la clase. Observa que las reglas de reescritura se han dise�ado sobre todo para evitar accidentes; a�n es posible, con el suficiente empe�o, leer o modificar una variable considerada privada. Esto puede ser �til, por ejemplo, para el depurador, por lo que no se ha cerrado esta puerta falsa. Hay un peque�o fallo: la derivaci�n de una clase con el mismo nombre que su clase base hace posible el uso de las variables privadas de la clase base.

Observa que el c�digo pasado a exec, eval() o evalfile() no considera el nombre de la clase llamante la clase actual. Es similar al efecto de la sentencia global, cuyo efecto est�, de igual manera, restringido al c�digo de un fichero. Se aplica la misma restricci�n a getattr(), setattr(), delattr() y tambi�n cuando se hace referencia a __dict__ directamente.

He aqu� un ejemplo de una clase que implementa sus propios m�todos __getattr__ y __setattr__ y almacena todos los atributos en una variable privada de manera que funciona adecuadamente en todas las versiones de Python, incluidas las anteriores a agregar esta caracter�stica:

class atributosVirtuales:
    __vdic = None
    __vdic_nombre = locals().keys()[0]
     
    def __init__(self):
        self.__dict__[self.__vdic_nombre] = {}
    
    def __getattr__(self, nombre):
        return self.__vdic[nombre]
    
    def __setattr__(self, nombre, valor):
        self.__vdic[nombre] = valor


9.7 Remates

A veces es �til tener un tipo de dato similar al ``record'' de Pascal o a la ``struct'' de C, que re�nan un par de datos con nombre. Para realizar esto, se puede usar una definici�n de clase vac�a, por ejemplo:

class Empleado:
    pass

juan = Empleado() # Creaci�n de una ficha de empleado vac�a

# Rellenamos los campos de la ficha
juan.nombre = 'Juan P�rez'
juan.departamento = 'Centro de c�lculo'
juan.sueldo = 1000

Si hay c�digo Python que espere recibir un tipo abstracto de datos concreto, es posible pasarle una clase que emule los m�todos de este tipo de dato, en lugar de la clase genuina. Por ejemplo, si disponemos de una funci�n que da formato a unos datos de un objeto fichero, podemos definir una clase con los m�todos read() y readline() que tome los datos de una cadena de almacenamiento temporal y pasar dicha clase como argumento.

Los objetos de m�todos de instancia tambi�n tienen atributos: m.im_self es el objeto del cual el m�todo es instancia y m.im_func es el objeto funci�n correspondiente al m�todo.


9.7.1 Las excepciones pueden ser clases

Las excepciones definidas por usuario ya no est�n limitadas a objetos cadena de texto; tambi�n pueden identificarse mediante clases. Utilizando estos mecanismos es posible crear jerarqu�as ampliables de excepciones.

Hay dos formas v�lidas nuevas de sentencia raise:

raise Clase, instancia

raise instancia

En la primera forma, instancia debe ser una instancia de Clase o de una clase derivada de ella. La segunda forma es un atajo de:

raise instancia.__class__, instancia

Una cl�usula except puede enumerar clases al igual que objetos cadena. Una clase de una cl�usula except captura una excepci�n si es de la misma clase que la excepci�n que ha saltado o es de una clase base de ella (al rev�s no; una clase derivada no captura ninguna de sus clases base). Por ejemplo, el c�digo a continuaci�n mostrar� B, C, D, en ese orden:

class B:
    pass
class C(B):
    pass
class D(C):
    pass

for c in [B, C, D]:
    try:
        raise c()
    except D:
        print "D"
    except C:
        print "C"
    except B:
        print "B"

Observa que si invertimos las cl�usulas ("except B" primero), se mostrar�a B, B, B, ya que la primera cl�usula captura todas las excepciones, por ser clase base de todas ellas.

Cuando se presenta un mensaje de error para una excepci�n sin capturar, se muestra el nombre de la clase, dos puntos, un espacio y, por �ltimo, la instancia convertida a cadena mediante la funci�n interna str().



Notas al pie

... nominal9.1
Excepto en una cosa. Los objetos de m�dulo tienen un atributo de s�lo lectura secreto llamado __dict__ que devuelve el diccionario utilizado para implementar el espacio nominal del m�dulo. El nombre __dict__ es un atributo, pero no un nombre global. Evidentemente, al utilizar esto se transgrede la abstracci�n de la implementaci�n del espacio nominal, por lo que su uso debe restringirse a herramientas especiales, como depuradores p�stumos.

Ver Sobre este documento... para obtener informaci�n sobre sugerencias.