6.1 Métodos mágicos
La sobrecarga de operadores en Python te permite definir o cambiar el comportamiento de los operadores incorporados (por ejemplo, +, -, *, /) para tus clases personalizadas. Esto se hace usando métodos especiales, también conocidos como métodos mágicos.
Por ejemplo, en tu clase puedes sobrecargar los operadores de comparación:
| Operador | Método sin subrayado | Firma del método |
|---|---|---|
| == | eq() |
__eq__(self, other) |
| != | ne() |
__ne__(self, other) |
| < | lt() |
__lt__(self, other) |
| <= | le() |
__le__(self, other) |
| > | gt() |
__gt__(self, other) |
| >= | ge() |
__ge__(self, other) |
Supongamos que has creado tu clase y quieres que los objetos de tu clase se comparen de la forma que tú decidas. Solo necesitas implementar en tu clase el método «__eq__», y Python lo llamará cada vez que los objetos de tu clase se comparen en el código.
Ejemplo:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# Uso
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2) # Mostrará: True
print(v1 == v3) # Mostrará: False
Cada vez que comparas dos de tus objetos, Python verifica si tienen implementada la función «__eq__». Si es así, la llama. Si no, simplemente compara las referencias a los objetos.
De hecho, en el ejemplo anterior está escrito (aunque hay que agregar la verificación de si el método existe):
# Uso
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2)) # Mostrará: True
print(v1.__eq__(v3)) # Mostrará: False
6.2 Lista de todos los operadores
En total, hay 6 grupos de operadores disponibles para sobrecarga.
Operadores aritméticos:
| Operador | Método sin subrayado | Firma del método |
|---|---|---|
| + | add |
__add__(self, other) |
| - | sub |
__sub__(self, other) |
| * | mul |
__mul__(self, other) |
| / | truediv |
__truediv__(self, other) |
| // | floordiv |
__floordiv__(self, other) |
| % | mod |
__mod__(self, other) |
| ** | pow |
__pow__(self, other) |
Operadores de comparación:
| Operador | Método sin subrayado | Firma del método |
|---|---|---|
| == | eq |
__eq__(self, other) |
| != | ne |
__ne__(self, other) |
| < | lt |
__lt__(self, other) |
| <= | le |
__le__(self, other) |
| > | gt |
__gt__(self, other) |
| >= | ge |
__ge__(self, other) |
Operadores lógicos:
| Operador | Método sin subrayado | Firma del método |
|---|---|---|
| & | and |
__and__(self, other) |
| | | or |
__or__(self, other) |
| ^ | xor |
__xor__(self, other) |
| ~ | invert |
__invert__(self) |
Operadores de indexación y cortes:
| Operador | Método |
|---|---|
| obj[key] | __getitem__(self, key) |
| obj[key] = value | __setitem__(self, key, value) |
| del obj[key] | __delitem__(self, key) |
Operadores unarios:
| Operador | Método |
|---|---|
| - | __neg__(self) |
| + | __pos__(self) |
| abs() | __abs__(self) |
| ~ | __invert__(self) |
Operadores de asignación:
| Operador | Método |
|---|---|
| += | __iadd__(self, other) |
| -= | __isub__(self, other) |
| *= | __imul__(self, other) |
| /= | __itruediv__(self, other) |
| //= | __ifloordiv__(self, other) |
| %= | __imod__(self, other) |
| **= | __ipow__(self, other) |
Tal vez por eso, Python es tan lento: cada vez que ejecuta un operador, busca una función análoga en la clase y todos sus padres. Pero esto permite escribir el código más compacto del mundo :)
6.3 Operador de indexación
Que puedas comparar objetos o restar conjuntos es, de alguna manera, obvio. Y tú mismo adivinas esto si escribes una clase que implica operaciones lógicas o matemáticas sobre ella.
Quiero discutir con ustedes un ejemplo interesante: el operador de indexación. Vamos a empezar con el ejemplo de código:
class CustomList:
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __delitem__(self, index):
del self.data[index]
def __repr__(self):
return repr(self.data)
# Uso
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1]) # Mostrará: 2
c_list[1] = 10
print(c_list) # Mostrará: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list) # Mostrará: [1, 3, 4, 5]
Aquí vemos un ejemplo de tres operaciones:
Lectura de datos por índiceEscritura de datos por índiceE incluso eliminación de datos por índice.
Y no es necesario que los datos se almacenarán dentro como una lista. O que el índice debe ser un número. Por ejemplo, la clase dictionary (diccionario) está implementada de esta manera.
Creando SuperList
¿Recuerdas la clase list? Puedes asignarle elementos, pero solo con aquellos índices que ya existen. Vamos a crear nuestra propia clase, la llamaremos SuperList, a cuyos elementos se puede acceder con cualquier índice:
Si el índice < 0, insertaremos el elemento al inicioSi el índice >= len, agregaremos el elemento al finalEn otros casos simplemente devolveremos el elemento
Ejemplo:
class SuperList(list):
def __init__(self, value):
super().__init__(value)
def __setitem__(self, index, value):
if index >= len(self):
super().append(value)
elif index < 0:
super().insert(0, value)
else:
super().__setitem__(index, value)
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst) # [99, 1, 2, 3, 100]
Así que la sobrecarga de índices es una gran oportunidad, y te recomiendo usarla en la práctica. Eso es todo por hoy.
GO TO FULL VERSION