jueves, 12 de noviembre de 2015

Sobrecarga de operadores en Python

La palabra sobrecarga tiene ligada en su esencia la  idea de saturación, de trabajar sobre un limite impuesto y con un desenlace no positivo. Pero cuando usamos este termino en la programación orientada a objetos (POO) veremos que es cuanto menos eso.

-¿Cómo es esto? ¿De qué estás hablando?

Pues sí, a ver, empecemos con una explicación sobre la operación más básica en la cartera del saber humano. La suma y su operador universal "+" . Todos sabemos sumar y Python por supuesto que no se queda atrás, al escribir en el interprete de Python:


>> 2 + 2
4

Y no solo eso, Python también es capaz de sumar cadenas de caracteres:

>>"Carro" +"Azul"
CarroAzul

A esto se le conoce como concatenación. Y cabe resaltar que no es conmutativa.

>> "Azul" + "Carro"
AzulCarro

-Uhmm ya, de ahí viene la expresión que dice: no es lo mismo un metro de encaje negro que un negro te encaje el me....


Sí bueno, exacto. Aquí se empieza a ver entonces la posibilidad que tiene el operador + para que actúe según los operandos. Pareciera que de alguna forma Python sabe que debe hacer.

-Aja, y que pasa si trato de sumar 5 + azul.

Pues, que Python se quejará, y con razón. Ya que si lo que se pretendía era que concatenara 5azul, entonces se debió primero convertir el 5 en una cadena de caracteres.

Aunque existen algunas "sumas" no validas el hecho es que la suma también aparece definida para otros tipos de objetos, como por ejemplo las listas:

>> [1, 2] + [3, 4]
[1, 2, 3, 4]

A donde quiero llegar con todo esto es que con un mismo operador se pueden realizar múltiples operaciones de distinta índole, estando definida la operación por el tipo de objeto al que es aplicada: si son dos números realiza suma algebraica, si son cadenas de texto concatena, etc.  Esto no es más que la sustancia de toda esta cuestión. Un mismo operador y muchas maneras de comportarse.

Esta avanzada filosofía de programación es lo que se conoce como sobrecarga de operadores, y junto con la sobrecarga de métodos conforman uno de los pilares más sofisticados de la POO, algo conocido como polimorfismo.

-Hmm sí, entiendo. Pero no veo como esto puede serme útil.

La utilidad de todo esto, radica en que se pueden sobrecargar aun más los operadores. Es decir podemos implementar sumas de objetos pertenecientes a clases creadas por nosotros.

-Explícate mejor.

A ver, empecemos con un ejemplo sencillo. Queremos crear una clase que represente los vectores libres en el plano. Es decir vectores pertenecientes al plano R2 tal que v = (x,y) Además de esto  sería intuitivamente práctico poder definir una operación suma de dos vectores.

De tal suerte que si definimos v1 = (5,6) y v2 = (3,4) al aplicar la operación v3 = v1+v2 obtengamos v3 = (8,10)

Primero empecemos definiendo nuestra clase vector:

class vector:
         def __init__(self, x, y)
           self.x = x
           self.y = y 

voailá, tenemos lista nuestra espartana clase vector, que únicamente contiene el inicializador que se ocupa de crear las variables miembro x e y referenciando a los valores facilitados durante la creación del objeto.

Para que esta clase realice la sobrecarga  al operador ‘+’ y podamos hablar de suma de dos vectores necesitamos incluir en la clase un método especial: __add__. Al igual que __init__, observa que comienza y termina con dos guiones bajos, indicando que es un nombre con un significado especial para Python.

El código que incluyamos en ese método especial determinará cómo se comportará la clase ante la operación suma.

Es importante tener muy claro algo antes. La suma objeto1 + objeto2 es equivalente a aplicar el método __add__ sobre objeto1, recibiendo como argumento objeto2. Es decir:

objeto1.__add__(objeto2)

Asegúrense de entender esto bien, ya que es todo el misterio que tiene la sobrecarga de operadores: objeto1, el operando de la izquierda del +, debe tener un método __add__ que recibirá como argumento el operando de la derecha, objeto2.  A modo de ayuda aquí les dejo un enlace con algunos de los métodos especiales más comunes en Python, recopilados a manos de la gente de escomposlinux [ir al enlace]  

Y si todavía no tienes muy claro esto de los métodos especiales y como usarlos la gente de librosweb tiene un capitulo dedicado a este tema. [ir al enlace] (Si estás empezando en Python recomiendo su lectura)  

Nuestra clase con la suma sobrecargada nos queda así:



class Vector: 
            def __init__(self, x, y): 
                 self.x = x 
                 self.y = y 
            def __add__(self, v):
                 sumax = self.x + v.x
                 sumay = self.y + v.y 
                 vectorsuma = Vector(sumax, sumay) 
                 return vectorsuma


Analicemos con cuidado el código del método __add__:

Aparte de self, presente en todos los métodos como primer parámetro, y que referenciará al objeto actual cuando se cree (el de la izquierda del +), observa que toma otro parámetro, que hemos denominado v, que no es otra cosa sino el vector a la derecha del +. En el código de la función distinguirás como, por un lado, nos referimos a las componentes x e y del objeto propio como self.x y self.y, mientras que las del segundo operando, facilitado como argumento, son v.x y v.y.

El constructor de nuestra clase ,Vector es el que se encarga de componer nuestro resultado de las sumas en x e y presentándolas en el formato propio que hemos definido para los vectores, esto es (x,y)  

Veamos entonces nuestra clase completa con un ejemplo de su uso: 

class Vector: 
            def __init__(self, x, y): 
                 self.x = x 
                 self.y = y 
            def __add__(self, v):
                 sumax = self.x + v.x
                 sumay = self.y + v.y 
                 vectorsuma = Vector(sumax, sumay) 
                 return vectorsuma


>> v1 = vector(1,3)
>> v2 = vector(4,2)
>> v3 = v1 + v2
>> print v3
(5,5)

¡Genial! Ahora como complemento y para que refuercen este tema les dejo como ejercicio que sobrecarguen el operador * de nuestra clase vector, para que realice el producto escalar entre dos vectores. 

Y un problema un poco más avanzado, creen una nueva clase para vectores en R3, y sobrecarguen el operador * para que se pueda realizar el producto vectorial entre dos vectores de esa clase.


No hay comentarios:

Publicar un comentario

¿Dudas?