Python: Métodos especiales de clases

Está página este pensada para ser leida como una continuación al tutorial de Python de Guido Van Rossum, que puede obtenerse en http://www.python.org. y en esta misma web en la sección de El Bazar. Trata algunos conceptos algo más avanzados (aunque no por ello muy difíciles de entender) que nos permitirán aprovechar las características más avanzadas del lenguaje.

Nota: A lo largo de esta guía se utilizará el nombre "objeto" para referinos a una instancia de una clase que contiene el método que estemos tratando.

Las clases tienen una serie de métodos con un significado especial. Por ejemplo, ya conocemos el significado del método __init__() que se utiliza como constructor de clase, pero hay otros también muy interesantes. Vamos a verlos:

Personalización Básica

__init__(self, args...)

Constructor del objeto. Si es una clase derivada, es importante llamar al método __init__ de la clase base dentro del __init__ de la clase derivada, para asegurar una inicialización adecuada.

__del__(self)

Es el destructor del objeto. Se le llama cuando la instancia va a ser borrada. De nuevo, si la clase base tiene un método __del__, el de la clase derivada debe llamarlo para asegurar una limpieza adecuada. No se garantiza que los métodos __del__() de todas las instancias de clase existentes puedan ser llamados en el momento en el que el interprete acabe.

del x no tiene porqué llamar automáticamente al destructor del objeto x. Lo que hace es decrementar la cuenta de referencias a x, y si esa cuenta llega a cero entonces si que se llama a __del__.

Dadas las circunstancias bajo las que se llama a __del__, dentro de este las excepciones son ignoradas (se imprime una advertencia en sys.stderr). Por ello __del__ debería realizar lo mínimo imprescindible en cuanto a la limpieza del objeto.

__repr__(self)

Método para convertir el objeto a una cadena, de modo que podamos llamar a la función interena repr() (comillas invertidas) sobre el objeto. Normalmente esta cadena tiene el aspecto de una expresión Python que podría utilizarse para recrear otro objeto con el mismo valor, o una cadena descriptiva en el caso de objetos complejos que podrían tener una representación muy complicada.

__str__(self)

Este método también debería devolver una representación en forma de cadena del objeto. Se le llama cuando imprimimos una instancia del objeto con print o llamamos a la función interna str() sobre el mismo. A diferencia de __repr()__ la cadena resultante no necesita ser una expresión Python válida.

Ejemplo:

class prueba:

   def __init__(self):
		self.a = 0
   def __inc__(self, cuanto = 1):
       self.a = self.a + cuanto
   def __str__(self):
       return str(a)
p = prueba()
p.inc()
print p.a
[devuelve 1]
print p
[devuelve 1]
__cmp__(self,otro) (¡actualizar!)

Este método es llamado por operaciones de comparación. Debe devolver un entero negativo si self < otro, cero si self == otro y un entero positivo si self > otro. Si no se ha definido __cmp__ los objetos son comparados por su identidad (su dirección de memoria).

__hash__(self)

Se utiliza para obtener la clave del objeto si es insertado en un diccionario, o se llama a la función interna hash() sobre él. Debe devolver un entero de 32 bits. Es importante que se cumpla que todos los objetos que se __comparan__ con igualdad devuelvan el mismo valor de __hash__. Si un objeto no define __cmp__ tampoco debería definir __hash__. Si define __cmp__ pero no __hash__, sus instancias no podrán utilizarse como claves en diccionarios. Si una clase define objetos mutables e implementa un método __cmp__ no debe implementar un __hash__ dado que las claves de los diccionarios deben de ser objetos inmutables.

__nonzero__(self)

Se utiliza para implementar la comprobación de certeza. Debe devolver 0 o 1 (falso o verdadero). Cuando este método no está definido, se utiliza __len__() si está definido. Si en una clase no están definidos __nonzero__ ni __len__(), todas sus instancias se tomarán como ciertas (valor 1).

Emulando objetos invocables

__call__(self,[args])

Se utiliza para hacer que el objeto pueda ser llamado (como una función), de modo que si tenemos una instancia x que define __call__(self, valor) podemos hacer x(valor), lo que en realidad es un atajo a x.__call__(valor).

Emulando tipos secuenciales y mapeables [Revisar]

__len__(self)

Utilizada para implementar la función interna len(). Debe devolver un entero >=0 que indique la longitud del objeto. Los métodos que no definen un método __nonzero__() y cuyo método __len__ devuelve 0 se consideran falsos en un contexto booleano (todos los demas se considerarán ciertos).

__getitem__(self, clave)

Se utiliza para implementar la evaluación de objeto[clave]. Para tipos secuenciales, las claves aceptadas deberían ser enteros. Nótese que la interpretación especial de índices negativos, depende del la interpretación que __getitem__ haga de los mismos.

__setitem__(self, clave, valor)

Se utiliza para implementar la asignación a objeto[clave]. Sólo debería utilizarse con mapeados si el objeto soporta cambios en los valores de las claves (no en la misma clave) o si pueden añadirse nuevas claves, y sólo con secuencias si los elementos de estas pueden reemplazarse.

__delitem__(self, clave)

Se utiliza para implementar el borrado de objeto[clave]. Sólo para mapeados en los que el objeto permite borrado de claves, o para secuencias que permitan borrados de elementos.

Métodos especiales para la emulación de tipos secuenciales

Los tipos secuenciales inmutables sólo deberían definir __getslice__. Los tipos secuenciales mutables deben definir los tres siguientes:

__getslice__(self, i, j)

Se utiliza para implementar la evaluación de objeto[i:j]. El objeto devuelto debe ser del mismo tipo que "objeto". Nótese que en caso de faltar alguno de los valores se sustituirá i por 0 o j por sys.maxint. La interpretación de índices negativos o índices mayores que la longitud de la secuencia es responsabilidad del método.

__setslice__(self, i, j, secuencia)

Se utiliza para implementar la asignación a objeto[i:j]. Se aplican las mismas notas a i y j que a __getslice__.

__delslice__(self, i, j)

Utilizado para implementar el borrado de objeto[i:j]. Se aplican las mismas notas a i y j que a __getslice__.

Emulando tipos numéricos

__add__(self, otro)

__sub__(self, otro)

__mul__(self, otro)

__div__(self, otro)

__mod__(self, otro)

__divmod__(self, otro)

__pow__(self, otro, [modulo])

__lshift__(self, otro)

__rshift__(self, otro)

__and__(self, otro)

__xor__(self, otro)

__or__(self, otro)

Las funciones anteriores implementan los peradores aritméticos binarios +, -, *, /, % ,divmod , pow(), **, <<, >>, &, [comilla], |. Por ejemplo x+y llamaría al método x.__add__(y)

__radd__(self, otro)

__rsub__(self, otro)

__rmul__(self, otro)

__rdiv__(self, otro)

__rmod__(self, otro)

__rdivmod__(self, otro)

__rpow__(self, otro)

__rlshift__(self, otro)

__rrshift__(self, otro)

__rand__(self, otro)

__rxor__(self, otro)

__ror__(self, otro)

Implementan las operaciones anteriores, pero sólo si el operando de la izquierda no implementa las operaciones binarias aritméticas. Por ejemplo, si en x+y x no implementa __add__ se llamará y.__radd__(x).

__neg__(self)

__pos__(self)

__abs__(self)

__invert__(self)

Se utiliza para implementar las operaciones unarias -,+, abs(), y inversión.

__complex__(self)

__int__(self)

__long__(self)

__float__(self)

Para implementar las funciones de conversión internas complex(), int(), long() y float(). Deben devolver un valor del tipo adecuado.

__oct__(self)

__hex__(self)

Para implementar las funciones internas oct() y hex(). Deben devolver una cadena.

[Falta __coerce__]

Personalizando los accesos a atributos (¡incluir propiedades!)

Los métodos que se detallan a continuación se utilizan para definir la forma en la que se accede a un atributo (bien por uso, asignación o borrado) x.nombre para las intancias de la clase. Por motivos de rendimiento, estos métodos se cachean en el objeto de la clase cuando esta se define y por lo tanto no pueden cambiarse una vez que la definición de la clase se ejecuta.

__getattr__(self, nombre)

Se le llama cuando no se encuentra un atributo por medio de los mecanismos normales de búsqueda (atributos de una instancia o en el arbol de la clase). 'nombre' es el nombre del atributo. Este método debería devolver el valor calculado del atributo o lanzar una excepción de tipo AtributeError.

Nótese que si el atributo se encuentra por medio de los mecanismos normales no se llama a __getattr__().

Por ejemplo: class prueba: def __init__(self): self.existente = 1 def __getattr__(self, nombre): print 'Creando atributo', nombre, 'y devolviendo el valor 3' self.nombre = 3 return self.nombre p = prueba() print .p.existente [Escribe "1"] p.otro_existente = 2 print p.otro_existente [Escribe "2"] print p.dinamico [Escribe "Creando atributo dinamico y devolviendo el valor 3" y despues escribe "3"] print p.dinamico [Escribe "3"] __setattr__(self, nombre, valor)__

Se le llama cuando se intenta una asignación a un atributo, en lugar de proceder al mecanismo normal (almacenar el valor en el diccionario de atributos de la instancia). 'nombre' es el nombre del atributo, y no debería hacer "self.nombre = valor" porque esto ocasionaría una llamada recursiva a si mismo, sino insertar el valor en el diccionario de los atributos de las instancia (self.__dict__[nombre] = valor).



__delattr__(self, nombre)

Igual que __setattr__ pero para el borrado de atributos en lugar de para su asignación. Este método sólo debería implementarse si la expresión "del objeto.nombre" tiene algún significado para el objeto.