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 +
operador realizará la suma aritmética de dos números, fusionará dos listas o concatenará dos cadenas.
Esta característica en Python que permite que el mismo operador tenga un significado diferente según el contexto se llama sobrecarga de operador.
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 (última llamada más reciente): Archivo "", línea 9, en letra impresa (p1 + p2) TypeError: tipo (s) de operando no admitido para +: 'Point' y 'Point'
Aquí, podemos ver que TypeError
se generó a, ya que Python no sabía cómo sumar dos Point
objetos.
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
Las funciones de clase que comienzan con doble subrayado __
se denominan funciones especiales en Python.
Estas funciones no son las funciones típicas que definimos para una clase. La __init__()
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)
Supongamos que queremos que la print()
función imprima las coordenadas del Point
objeto en lugar de lo que obtuvimos. Podemos definir un __str__()
método en nuestra clase que controle 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 intentemos la print()
función 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) 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 incorporada str()
o format()
.
>>> str(p1) '(2,3)' >>> format(p1) '(2,3)'
Entonces, cuando usa str(p1)
o format(p1)
, Python llama internamente al p1.__str__()
método. De ahí el nombre, funciones especiales.
Ahora volvamos a la sobrecarga del operador.
Sobrecarga del operador +
Para sobrecargar el +
operador, necesitaremos implementar la __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, p1.__add__(p2)
que a su vez es Point.__add__(p1,p2)
. Después de esto, la operación de adición se lleva a cabo de la forma que especificamos.
Del mismo modo, también podemos sobrecargar a otros operadores. La función especial que debemos implementar se tabula 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) |
Poder | p1 ** p2 | p1.__pow__(p2) |
División | p1 / p2 | p1.__truediv__(p2) |
División de piso | p1 // p2 | p1.__floordiv__(p2) |
Resto (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) |
Y bit a bit | p1 & p2 | p1.__and__(p2) |
O bit a bit | p1 | p2 | p1.__or__(p2) |
XOR bit a bit | p1 p2 | p1.__xor__(p2) |
Bit a bit NO | ~p1 | p1.__invert__() |
Operadores de comparación de sobrecarga
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 <
en nuestra 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
Output
True False False
Similarly, the special functions that we need to implement, to overload other comparison operators are tabulated below.
Operator Expression Internally
Less than p1 < p2
p1.__lt__(p2)
Less than or equal to p1 <= p2
p1.__le__(p2)
Equal to p1 == p2
p1.__eq__(p2)
Not equal to p1 != p2
p1.__ne__(p2)
Greater than p1> p2
p1.__gt__(p2)
Greater than or equal to p1>= p2
p1.__ge__(p2)