Sobrecarga del operador de Python
Sobrecarga del operador de Python
Puede cambiar el significado de un operador en Python según los operandos utilizados. En este tutorial, aprenderá a utilizar la sobrecarga de operadores en la programación orientada a objetos de Python.
Sobrecarga del operador de Python
Los operadores de Python funcionan para clases integradas. Pero el mismo operador se comporta de manera diferente con diferentes tipos. Por ejemplo, el +
el operador realizará sumas aritméticas en dos números, fusionará dos listas o concatenará dos cadenas.
Esta función de Python que permite que el mismo operador tenga un significado diferente según el contexto se denomina sobrecarga de operadores.
Entonces, ¿qué sucede cuando los usamos con objetos de una clase definida por el usuario? Consideremos la siguiente clase, que intenta simular un punto en un sistema de coordenadas 2-D.
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
Salida
Traceback (most recent call last): File "<string>", line 9, in <module> print(p1+p2) TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
Aquí, podemos ver que un TypeError
se planteó, ya que Python no sabía cómo agregar dos Point
objetos juntos.
Sin embargo, podemos lograr esta tarea en Python mediante la sobrecarga de operadores. Pero primero, tengamos una noción sobre las funciones especiales.
Funciones especiales de Python
Funciones de clase que comienzan con doble guión bajo __
se llaman funciones especiales en Python.
Estas funciones no son las típicas funciones que definimos para una clase. El __init__()
La función que definimos anteriormente es una de ellas. Se llama cada vez que creamos un nuevo objeto de esa clase.
Hay muchas otras funciones especiales en Python. Visite Funciones especiales de Python para obtener más información sobre ellas.
Usando funciones especiales, podemos hacer que nuestra clase sea compatible con las funciones integradas.
>>> p1 = Point(2,3)
>>> print(p1)
<__main__.Point object at 0x00000000031F8CC0>
Supongamos que queremos el print()
función para imprimir las coordenadas del Point
objeto en lugar de lo que tenemos. Podemos definir un __str__()
en nuestra clase que controla cómo se imprime el objeto. Veamos cómo podemos lograr esto:
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x,self.y)
Ahora probemos el print()
funcionar de nuevo.
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0}, {1})".format(self.x, self.y)
p1 = Point(2, 3)
print(p1)
Salida
(2, 3)
Eso es mejor. Resulta que este mismo método se invoca cuando usamos la función integrada str()
o format()
.
>>> str(p1)
'(2,3)'
>>> format(p1)
'(2,3)'
Entonces, cuando usas str(p1)
o format(p1)
, Python llama internamente al p1.__str__()
método. De ahí el nombre, funciones especiales.
Ahora volvamos a la sobrecarga de operadores.
Sobrecarga del operador +
Para sobrecargar el +
operador, necesitaremos implementar __add__()
función en la clase. Con un gran poder viene una gran responsabilidad. Podemos hacer lo que queramos, dentro de esta función. Pero es más sensato devolver un Point
objeto de la suma de coordenadas.
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x, self.y)
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Point(x, y)
Ahora intentemos la operación de suma nuevamente:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x, self.y)
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Point(x, y)
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
Salida
(3,5)
Lo que realmente sucede es que, cuando usas p1 + p2
, Python llama a p1.__add__(p2)
que a su vez es Point.__add__(p1,p2)
. Luego de esto, la operación de suma se realiza de la forma que especificamos.
Del mismo modo, también podemos sobrecargar a otros operadores. La función especial que necesitamos implementar está tabulada a continuación.
Operador | Expresión | Internamente |
---|---|---|
Adición | p1 + p2 | p1.__add__(p2) |
Sustracción | p1 - p2 | p1.__sub__(p2) |
Multiplicación | p1 * p2 | p1.__mul__(p2) |
Potencia | p1 ** p2 | p1.__pow__(p2) |
División | p1 / p2 | p1.__truediv__(p2) |
División de piso | p1 // p2 | p1.__floordiv__(p2) |
Restante (módulo) | p1 % p2 | p1.__mod__(p2) |
Desplazamiento a la izquierda bit a bit | p1 << p2 | p1.__lshift__(p2) |
Desplazamiento a la derecha bit a bit | p1 >> p2 | p1.__rshift__(p2) |
Bitbit Y | p1 & p2 | p1.__and__(p2) |
Bitbit O | p1 | p2 | p1.__or__(p2) |
XOR bit a bit | p1 ^ p2 | p1.__xor__(p2) |
Bitbit NO | ~p1 | p1.__invert__() |
Sobrecarga de operadores de comparación
Python no limita la sobrecarga de operadores solo a operadores aritméticos. También podemos sobrecargar los operadores de comparación.
Supongamos que quisiéramos implementar el símbolo menor que <
símbolo en nuestro Point
clase.
Comparemos la magnitud de estos puntos desde el origen y devolvamos el resultado para este propósito. Se puede implementar de la siguiente manera.
# overloading the less than operator
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return "({0},{1})".format(self.x, self.y)
def __lt__(self, other):
self_mag = (self.x ** 2) + (self.y ** 2)
other_mag = (other.x ** 2) + (other.y ** 2)
return self_mag < other_mag
p1 = Point(1,1)
p2 = Point(-2,-3)
p3 = Point(1,-1)
# use less than
print(p1<p2)
print(p2<p3)
print(p1<p3)
Salida
True False False
Del mismo modo, las funciones especiales que necesitamos implementar para sobrecargar otros operadores de comparación se tabulan a continuación.
Operador | Expresión | Internamente |
---|---|---|
Menos de | p1 < p2 | p1.__lt__(p2) |
Menor o igual que | p1 <= p2 | p1.__le__(p2) |
Igual a | p1 == p2 | p1.__eq__(p2) |
No es igual a | p1 != p2 | p1.__ne__(p2) |
Mayor que | p1 > p2 | p1.__gt__(p2) |
Mayor o igual que | p1 >= p2 | p1.__ge__(p2) |
python