Sobrecarga De Operadores Python 3. Clase Fracciones

por | 23 diciembre, 2013

Hola Como ahora tengo mucho tiempo me he dedicado a investigar sobre la sobrecarga de operadores en Python 3. Yo creía que esto no se podía hacer, pero resulta que sí . Para practicar he empezado con algo sencillo, creando una clase fracciones para poder operar con ellas. Cierto es que esto ya viene implementado en Python importando ‘fractions’, pero se trata de hacerlo nosotros para aprender la sobrecarga de operadores, así podemos implementar cosas como los cuaterniones. Para no hacerlo muy extenso sólo he contemplado el poder operar con dos fracciones con sus elementos enteros.
Pero, ¿qué es un operador?, buena pregunta. Para responderla con fundamento debemos mirar hacia las matemáticas, sí, hacia las matemáticas. En realidad un operador no es más que una aplicación que cumple ciertas condiciones. Los operadores pueden ser: unarios, binarios, ternarios, etc. Un ejemplo de operador binario puede ser el operador suma ‘+’ entre dos números enteros cualesquiera. En la vida cotidiana la solemos nombrar como ‘operación suma’. Así pues, podemos definir el operador suma de enteros como la aplicación:
+:\mathbb{Z}\times\mathbb{Z}\longrightarrow\mathbb{Z}
la cual aplica:
+(a,\: b) := a+b\:\forall\:a,\: b\in\mathbb{Z}
En el caso de las fracciones de nuestro código nuestra aplicación (operador) suma es:
+:\mathbb{Q}\times\mathbb{Q}\longrightarrow\mathbb{Q}
la cual aplica:
+\left( \frac{p}{q} ,\: \frac{r}{s} \right) := \frac{p}{q} +\frac{r}{s}=\dfrac{m:q\cdot r+m:s\cdot r}{m}\quad m=mcd(p,\: q)\: ; \forall\: \frac{p}{q},\: \frac{r}{s}\in\mathbb{Q} ;\quad p,\: r\in\mathbb{Z},\: q,\: s\in\mathbb{Z}-{0}
De la misma forma tenemos los operadores binarios: resta, producto, división. Ejemplos de operadores unarios pueden ser: opuesto, inverso, valor absoluto, etc..
Una vez aclarado el concepto de operador debemos preguntarnos para qué queremos sobrecargar operadores en una lenguaje de programación, en nuestro caso Python. La cosa es sencilla, en nuestro código si tenemos 2 números cualesquiera reales podemos escribir directamente a+b y la operación se realizará. Pero si queremos, por ejemplo, sumar dos fracciones y nos devuelva directamente una fracción; a no ser que alguien lo haya implementado, no lo tenemos (bueno en éste caso Python sí lo tiene, pero imaginemos que no).
Si implementamos una clase Fraccion y sobrecargamos el operador suma en nuestro código, podremos escribir P+Q y nos hará la operación devolviéndonos una fracción.
Otra cosa importante de la sobrecarga de los operadores es que en nuestro código funcionará cuando escribamos directamente P+Q sin tener que declarar una función propia llamada, por ejemplo; suma, y que para sumar en Python debemos ejecutar suma(P,Q). Como se ve es un adelanto enorme.
Muy bien, ahora queda saber cómo implementar la sobrecarga de operadores en Python. Por ejemplo, para sobrecargar el operador suma definimos una función __add__ de la siguiente forma:

  1. def __add__(self, frac):

Al terminar la definición de la función, como podemos sumar de derecha a izquierda y al revés (lo que se conoce en matemáticas como pre-sumar y post-sumar) debemos añadir la línea de código:

  1. __radd__ = __add__

Esto es porque por defecto en Python la suma está definida como pre-suma, así que le decimos que la post-suma (suma por la derecha, radd == right add) se define igual que la pre-suma (suma por la izquierda). Esto no es necesario para los unarios, como el operador opuesto. En el código se ha creado una clase llamada Fraccion con los operadores: suma, resta, producto, división, potencia, opuesto, inverso, valor absoluto y los de comparación. El código es el siguiente:

  1. #/usr/bin/env python3.3.2
  2. # -*- coding: utf-8 -*-
  3.  
  4. from fractions import gcd
  5.  
  6.  
  7. class Fraccion(object):
  8.     __slots__ = ['num', 'den']
  9.  
  10.     def __init__(self, num, den=1):
  11.         self.num = num
  12.         self.den = den
  13.  
  14.     def entrada(self):
  15.         frac = raw_input('Introduce una fracción:\n')
  16.         if '/' not in frac:
  17.             frac = frac + '/1'
  18.         l1 = frac.rsplit('/')
  19.         self.num = int(l1[0])
  20.         self.den = int(l1[1])
  21.         if self.num < 0 and self.den < 0:
  22.             self.num = -self.num
  23.             self.den = -self.den
  24.         elif self.num > 0 and self.den < 0:
  25.             self.num = -self.num
  26.             self.den = -self.den
  27.         l1 = []
  28.  
  29.     def __mul__(self, frac):
  30.         mult = Fraccion(self.num, self.den)
  31.         mult.num = self.num * frac.num
  32.         mult.den = self.den * frac.den
  33.         mult = mult.simplifica()
  34.         return mult
  35.  
  36.     __rmul__ = __mul__
  37.  
  38.     def __div__(self, frac):
  39.         divi = Fraccion(self.num, self.den)
  40.         frac = ~frac
  41.         divi = divi * frac
  42.         divi = divi.simplifica()
  43.         return divi
  44.  
  45.     __rdiv__ = __div__
  46.  
  47.     def __pow__(self, exp):
  48.         pot = Fraccion(self.num, self.den)
  49.         pot.num = self.num ** exp
  50.         pot.den = self.den ** exp
  51.         pot = pot.simplifica()
  52.         return pot
  53.  
  54.     def __add__(self, frac):
  55.         suma = Fraccion(self.num, self.den)
  56.         if self.den == frac.den:
  57.             suma.num = self.num + frac.num
  58.             suma = suma.simplifica()
  59.             return suma
  60.         else:
  61.             mcm1 = suma.mcm(self.den, frac.den)
  62.             suma.num = (mcm1 / self.den) * self.num +\
  63.             (mcm1 / frac.den) * frac.num
  64.             suma.den = mcm1
  65.             suma = suma.simplifica()
  66.             return suma
  67.  
  68.     __radd__ = __add__
  69.  
  70.     def __sub__(self, frac):
  71.         resta = Fraccion(self.num, self.den)
  72.         frac = -frac
  73.         resta = resta + frac
  74.         resta = resta.simplifica()
  75.         return resta
  76.  
  77.     __rsub__ = __sub__
  78.  
  79.     def __neg__(self):
  80.         op = Fraccion(self.num, self.den)
  81.         op.num = -self.num
  82.         op.den = self.den
  83.         return op
  84.  
  85.     def __invert__(self):
  86.         inv = Fraccion(self.num, self.den)
  87.         inv.num = self.den
  88.         inv.den = self.num
  89.         return inv
  90.  
  91.     def __abs__(self):
  92.         absoluto = Fraccion(self.num, self.den)
  93.         if self.num > 0 and self.den > 0:
  94.             return absoluto
  95.         elif self.num < 0:
  96.             absoluto.num = -self.num
  97.             return absoluto
  98.         elif self.den < 0:
  99.             absoluto.den = -self.den
  100.             return absoluto
  101.  
  102.     def mcm(self, a, b):
  103.         mincm = (a * b) / gcd(a, b)
  104.         return mincm
  105.  
  106.     def __lt__(self, frac):
  107.         c1 = self.num * frac.den
  108.         c2 = self.den * frac.num
  109.         if c1 < c2:
  110.             return True
  111.         else:
  112.             return False
  113.  
  114.     def __le__(self, frac):
  115.         c1 = self.num * frac.den
  116.         c2 = self.den * frac.num
  117.         if c1 <= c2:
  118.             return True
  119.         else:
  120.             return False
  121.  
  122.     def __eq__(self, frac):
  123.         c1 = self.num * frac.den
  124.         c2 = self.den * frac.num
  125.         if c1 == c2:
  126.             return 'Fracciones Equivalentes'
  127.         else:
  128.             return 'Fracciones No Equivalentes'
  129.  
  130.     def __ne__(self, frac):
  131.         c1 = self.num * frac.den
  132.         c2 = self.den * frac.num
  133.         if c1 != c2:
  134.             return True
  135.         else:
  136.             return False
  137.  
  138.     def __gt__(self, frac):
  139.         c1 = self.num * frac.den
  140.         c2 = self.den * frac.num
  141.         if c1 > c2:
  142.             return True
  143.         else:
  144.             return False
  145.  
  146.     def __ge__(self, frac):
  147.         c1 = self.num * frac.den
  148.         c2 = self.den * frac.num
  149.         if c1 >= c2:
  150.             return True
  151.         else:
  152.             return False
  153.  
  154.     def simplifica(self):
  155.         simp = Fraccion(self.num, self.den)
  156.         mcd = gcd(self.num, self.den)
  157.         simp.num = self.num / mcd
  158.         simp.den = self.den / mcd
  159.         return(simp)
  160.  
  161.     def __repr__(self):
  162.         if self.num == self.den:
  163.             return('1')
  164.         elif self.den == 1:
  165.             return('{}'.format(self.num))
  166.         elif self.num &gt; 0 and self.den &lt; 0:
  167.             return('{}/{}'.format(-self.num, -self.den))
  168.         else:
  169.             return('{}/{}'.format(self.num, self.den))
  170.  
  171.  
  172. p = Fraccion(0, 0)
  173. q = Fraccion(0, 0)
  174.  
  175. Fraccion.entrada(p)
  176. Fraccion.entrada(q)
  177.  
  178.  
  179. print('p*q = {}'.format(p * q))
  180. print('p/q = {}'.format(p / q))
  181. print('p+q = {}'.format(p + q))
  182. print('p-q = {}'.format(p - q))
  183. print('-p = {}'.format(-p))
  184. print('1/p = {}'.format(~p))
  185. print('p^2 = {}'.format(pow(p, 2)))
  186. print('|p| = {}'.format(abs(p)))
  187. print('Comparaciones:\n')
  188. print('{} < {} : {}'.format(p, q, p < q))
  189. print('{} < = {} : {}'.format(p, q, p <= q))
  190. print('{} = {} : {}'.format(p, q, p == q))
  191. print('{} ! = {} : {}'.format(p, q, p != q))
  192. print('{} > {} : {}'.format(p, q, p > q))
  193. print('{} > = {} : {}'.format(p, q, p >= q))

Por cierto, para implementarlo he utilizado el IDE llamado Ninja-IDE, porque te ayuda mucho para respetar las normas del PEP8 de Python. Saludos

Un pensamiento en “Sobrecarga De Operadores Python 3. Clase Fracciones

  1. Pingback: Bitacoras.com

Los comentarios están cerrados.