Sobrecarga de operadores

Los métodos especiales (no todos) son los siguientes:

  • __new__ - al crear una nueva instancia.
  • __init__ - justo después de crear la instancia.
  • __del__ - justo antes de destruir la instancia.
  • __repr__ - invocado por repr() para representar la instancia.
  • __str__ - invocado por str() para convertir la instancia a cadena.
  • __lt__ - al utilizar el operador "<"
  • __le__ - "<="
  • __eq__ - "=="
  • __ne__ - "!="
  • __gt__ - ">"
  • __ge__ - ">="
  • __cmp__ - para comparar la instancia con otro objeto. Retorna negativo, 0 o positivo se forma similar al strcmp() de C.
  • __hash__ - invocador por hash() para obtener un valor de hashing para la instancia.
  • __nonzero__ - invocado cuando se intenta convertir la instancia a un valor lógico.
  • __unicode__ - invocado por unicode() para convertir la instancia en una cadena unicode.
  • __getattr__(key) - para obtener un atributo de la instancia (si no existe)
  • __setattr__(key, value) - para dar valor a un atributo.
  • __delattr__ - para destruir un atributo.
  • __getattribute__(key, value) - para obtener un atributo de la instancia (aunque exista).https://docs.python.org/2/reference/datamodel.html
  • __call__ - al invocar la instancia (al usarla como una función).
  • __len__ - al usar len() para obtener la cantidad de elementos de un contenedor.
  • __getitem__(key) - al indexar la instancia para acceso, con y = instancia[x]
  • __setitem__(key, value) - al indexar la instancia para modificar, con instancia[x] = y
  • __delitem__ - al borrar un elemento indexado.
  • __iter__ - para devolver un iterador.
  • __reversed__ - al invocar reversed() para obtener un iterador desde el final.
  • __contains__ - al utilizar el operador "in" para comprobar si un objeto está en el contenedor.
  • __getslice__ - para aplicar un slice "x:y"
  • __add__ - al aplicar el operador de suma "+"
  • __sub__ - "-"
  • __mul__ - "*"
  • __mod__ - "%"
  • __pow__ - "**"
  • __lshift__ - "<<"
  • __rshift__ - ">>"
  • __and__ - "&"
  • __or__ - "|"
  • __xor__ - "^"
  • __iadd__ - "+=" y todas las correspondientes para los operadores anteriores: isub, imul, ...
  • __radd__ - En una expresión binaria (a + b) si el operador + (__add__) no está definido para "a" se busca el __radd__ para "b". Todos los operadores anteriores tienen esta modalidad: rsub, rmul, ...
  • __neg__ - "-" unario
  • __pos__ - "+" unario
  • __abs__ - al invocar abs()
  • __invert__ - "~"

Una clase Vector

La implementación de los operadores '+' y '*' de las listas Python difiere mucho de lo que podríamos esperar desde el punto de vista matemético:


In [16]:
a = [1, 2, 3]
print(a + [4, 5, 6])
print(a * 2)


[1, 2, 3, 4, 5, 6]
[1, 2, 3, 1, 2, 3]

Crearemos una clase Vector (como una especialización de list) para que estar operaciones se comporten como lo harían en un vector.


In [1]:
import itertools

class NotMatchingShapes(Exception):
    pass

class Vector(list):   
    def __add__(self, other):
        if len(self) != len(other):
            raise NotMatchingShapes
            
        return Vector([x+y for x,y in zip(self, other)])

v1 = Vector([1, 2, 3])
v1 + [4, 5, 6]


Out[1]:
[5, 7, 9]

In [14]:
class Vector(list):   
    def __add__(self, other):
        if len(self) != len(other):
            raise NotMatchingShapes
            
        return Vector([x+y for x,y in zip(self, other)])

    def __mul__(self, other):
        if isinstance(other, Vector):
            return Vector([x*y for x,y in zip(self, other)])
        
        return Vector([x*other for x in self])
    
    def __repr__(self):
        return "Vector({})".format(list.__repr__(self))
    
v1 = Vector([1, 2, 3])
print(v1 * 3)
print(v1 * Vector([4, 5 ,6]))


Vector([3, 6, 9])
Vector([4, 10, 18])