Los métodos mágicos aseguran un modelo de datos consistente que retiene la función heredada de la clase integrada mientras proporciona un comportamiento de clase personalizado. Estos métodos pueden enriquecer el diseño de la clase y mejorar la legibilidad del lenguaje. Entonces, en este artículo, veremos cómo hacer uso de los métodos mágicos, cómo funciona y los métodos mágicos disponibles en Python. Repasemos cada una de las secciones:
- Sintaxis del método mágico
- Métodos mágicos comunes
- Métodos mágicos para operadores binarios
- Métodos mágicos para operadores unarios
- Algunos otros métodos mágicos
Sintaxis del método mágico
Un método que está envuelto por dos guiones bajos en ambos lados se llama métodos mágicos. El motivo detrás del método mágico es sobrecargar los métodos integrados de Python y sus operadores. Aquí, _syntax evita que los programadores definan el mismo nombre para métodos personalizados. Cada método mágico cumple su propósito. Consideremos un ejemplo que verifica la equivalencia. Ejemplo:
Python3
class EquivalenceClass(object): def __eq__(self, other): return type(self) == type(other) print(EquivalenceClass() == EquivalenceClass()) print(EquivalenceClass() == 'MyClass')
Producción
True False
El método __eq__ toma dos argumentos, uno mismo y el objeto, para verificar la igualdad. Lo que es importante entender es que el método __eq__ se invoca cuando los dos objetos se comparan usando el operador ==. Repasemos algunos de los métodos mágicos comunes en python.
Métodos mágicos comunes
En Python, tenemos una amplia gama de métodos mágicos, cada uno cumple su propósito. Aquí revisaremos algunos de los métodos mágicos comunes:
- Creación
- Destrucción
- Conversión de tipo
- comparaciones
Creación
Los métodos mágicos enredados en la creación se realizan cuando se crea una instancia de clase. Dos de los métodos mágicos asociados son los métodos __init__ y __new__.
método __init__
El método __init__ de un objeto se ejecuta inmediatamente después de la creación de la instancia. Aquí, el método toma un argumento posicional, uno mismo, y cualquier cantidad de argumentos opcionales o de palabras clave. Veamos un ejemplo simple: Ejemplo:
Python3
class InitClass(object): def __init__(self): print('Executing the __init__ method.') ic = InitClass()
Producción
Executing the __init__ method.
Aquí, el punto esencial a tener en cuenta es que no está llamando al método __init__. En cambio, el intérprete de Python hace la llamada a la instanciación del objeto. Consideremos un ejemplo, que toma un argumento opcional:
Python3
class Square(object): def __init__(self, number = 2): self._number = number def square(self): return self._number**2 s = Square() print('Number: % i' % s._number) print('Square: % i' % s.square())
Producción
Number: 2 Square: 4
Aquí podemos notar que el método __init__ usa el valor predeterminado (2) en ausencia de un argumento opcional. Veamos algunos datos sobre el método __init__:
- El método __init__ proporciona datos iniciales al objeto, no para crear un objeto.
- Solo devuelve Ninguno; al devolver algo que no sea Ninguno, se genera TypeError.
- Personaliza la instanciación de una clase.
A continuación, procederemos al método __new__.
__Nuevo método
El método __new__ crea y devuelve la instancia de una clase. El argumento principal del método __new__ es la clase que se debe instanciar, y el resto son los argumentos mencionados durante la llamada de clase. Exploremos a través de un ejemplo: Ejemplo:
Python3
class Students(object): def __init__(self, idNo, grade): self._idNo = idNo self._grade = grade def __new__(cls, idNo, grade): print("Creating Instance") instance = super(Students, cls).__new__(cls) if 5 <= grade <= 10: return instance else: return None def __str__(self): return '{0}({1})'.format(self.__class__.__name__, self.__dict__) stud1 = Students(1, 7) print(stud1) stud2 = Students(2, 12) print(stud2)
Producción
Creating Instance Students({'_idNo': 1, '_grade': 7}) Creating Instance None
En la mayoría de los casos, no necesitamos definir un método __nuevo__. Si optamos por una implementación del método __new__, entonces es imprescindible hacer referencia a la superclase. Otro punto esencial a tener en cuenta, el método __init__ de la clase instanciada get se ejecuta solo si el método __new__ devuelve una instancia de la misma clase.
Destrucción
método __del__
El método __del__ se invoca al destruir una instancia de una clase, ya sea mediante la eliminación directa o la restauración de la memoria por parte del recolector de elementos no utilizados. Examinemos el siguiente código:
Python3
class MyClass(object): def __del__(self): print('Destroyed') MyClass() 'Immutable String - not assigned to a variable'
Producción
Destroyed
¿Qué pasa cuando creamos un objeto sin asignarle una variable? El recolector de elementos no utilizados mantendrá el registro de los objetos que no están referenciados a una variable y lo eliminará cuando se ejecute otra instrucción del programa. Aquí, creamos un objeto de MyClass sin asignarlo a una variable. Tras la ejecución de la declaración del programa (string inmutable, no asignada a una variable), el recolector de basura destruye el objeto MyClass. Lo mismo sucede, cuando eliminamos el objeto directamente; Pero aquí la eliminación ocurre de inmediato. Solo prueba el siguiente código. x = MiClase() del x
Conversión de tipo
La conversión de tipos se refiere a la conversión de un tipo de datos a otro; Python proporciona varios métodos mágicos para manejar la conversión.
- método __str__
- Métodos __int__, __float__ y __complex__
- método __bool__
método __str__
El método __str__ requiere un argumento posicional, self, y devuelve una string. Se llama cuando se pasa un objeto al constructor str(). Consideremos un ejemplo:
Python3
class MyString(object): def __str__(self): return 'My String !' print(str(MyString()))
Producción
My String!
Echemos un vistazo a otra situación que invoca el método __str__. El escenario es el uso de %s en una string de formato, que a su vez invoca el método __str__.
Python3
class HelloClass(object): def __str__(self): return 'George' print('Hello, % s' % HelloClass())
Producción
Hello, George
Métodos __int__, __float__ y __complex__
El método __int__ se ejecuta al llamar al constructor int y devuelve un int; Convierte los objetos complejos en tipo int primitivo. Del mismo modo, los métodos __float__ y _complex__ se ejecutan al pasar el objeto al constructor flotante y complejo, respectivamente.
método __bool__
El método mágico __bool__ en python toma un argumento posicional y devuelve verdadero o falso. Su propósito es verificar si un objeto es verdadero o falso, o convertirlo explícitamente a un valor booleano.
comparaciones
Los métodos mágicos de comparación se invocan cuando verificamos la equivalencia (==, !=) o las relaciones (<, y > =). Cada uno de estos operadores en python se asigna a sus métodos mágicos correspondientes.
Igualdad binaria
1. Método __eq__ El método __eq__ se ejecuta cuando se comparan dos objetos usando el operador ==. Se necesitan dos argumentos posicionales: el yo y el objeto para verificar la igualdad. En la mayoría de los casos, si se define el objeto del lado izquierdo, primero se comprueba su equivalencia. Veamos a través de un ejemplo:
Python3
class MyEquivalence(object): def __eq__(self, other): print('MyEquivalence:\n' '% r\n % r' %(self, other)) return self is other class YourEquivalence(object): def __eq__(self, other): print('Your Equivalence:\n' '% r\n % r' %(self, other)) return self is other eq1 = MyEquivalence() eq2 = YourEquivalence() # checking for equivalence where eq1 is at the left side print(eq1 == eq2) # checking for equivalence where eq2 is at the left side print(eq2 == eq1)
Producción
MyEquivalence: <__main__.MyEquivalence object at 0x7fa1d38e16d8> <__main__.YourEquivalence object at 0x7fa1d1ea37b8> False Your Equivalence: <__main__.YourEquivalence object at 0x7fa1d1ea37b8> <__main__.MyEquivalence object at 0x7fa1d38e16d8> False
La regla de ordenación no se aplica si un objeto es una subclase directa del otro. Examinemos a través de un ejemplo:
Python3
class MyEquivalence(object): def __eq__(self, other): print('MyEquivalence:\n' '% r\n % r' %(self, other)) return self is other class MySubEquivalence(MyEquivalence): def __eq__(self, other): print('MySubEquivalence:\n' '% r\n % r' %(self, other)) return self is other eqMain = MyEquivalence() eqSub = MySubEquivalence() # eqMain at the right side print(eqMain == eqSub) # eqSub at the right side print(eqSub == eqMain)
Producción
MySubEquivalence: <__main__.MySubEquivalence object at 0x7f299ce802b0> <__main__.MyEquivalence object at 0x7f299e8be6d8> False MySubEquivalence: <__main__.MySubEquivalence object at 0x7f299ce802b0> <__main__.MyEquivalence object at 0x7f299e8be6d8> False
2. Método __ne__ El método mágico __ne__ se ejecuta cuando se utiliza el operador !=. En la mayoría de los casos, no necesitamos definir el método __ne__; Al usar el operador !=, el intérprete de python ejecutará el método __eq__ e invertirá el resultado.
Comparaciones relativas: métodos __lt__ y __le__, __gt__ y __ge__
Los métodos __lt__ y __le__ se invocan cuando se utilizan los operadores < y <=, respectivamente. Y los métodos __gt__ y __ge__ se invocan al utilizar los operadores > y >=, respectivamente. Sin embargo, no es necesario usar todos estos 4 métodos; el uso de los métodos __lt__ y __gt__ cumplirá el propósito. Simplemente examine los puntos a continuación para comprender por qué no requerimos todos estos métodos: 1. Los métodos __ge__ y __le__ se pueden reemplazar con el inverso de los métodos __lt__ y __gt__, respectivamente. 2. La disyunción de los métodos __lt__ y __eq__ se puede usar en lugar del método __le__ y, de manera similar, los métodos __gt__ y __eq__ para el método __ge__. Echemos un vistazo al siguiente ejemplo. Aquí, compararemos el objeto en función de su tiempo de creación.
Python3
import time class ObjectCreationTime(object): def __init__(self, objName): self._created = time.time() self._objName = objName def __lt__(self, other): print('Creation Time:\n' '% s:% f\n % s:% f' %(self._objName, self._created, other._objName, other._created)) return self._created < other._created def __gt__(self, other): print('Creation Time:\n' '% s:% f\n % s:% f' %(self._objName, self._created, other._objName, other._created)) return self._created > other._created obj1 = ObjectCreationTime('obj1') obj2 = ObjectCreationTime('obj2') print(obj1 < obj2) print(obj1 > obj2)
Producción
Creation Time: obj1:1590679265.753279 obj2:1590679265.753280 True Creation Time: obj1:1590679265.753279 obj2:1590679265.753280 False
Métodos mágicos para operadores binarios
Veamos 3 métodos mágicos proporcionados por python para operadores binarios.
- Método vainilla
- Método inverso
- Método en el lugar
Método vainilla
Considere una expresión, x + y; En el método Vanilla, esta expresión se asigna a x.__add__(y). Consideremos otra expresión, y – x. Aquí, la expresión se asigna a y.__sub__(x). De manera similar, a * b se asigna a a.__mul__(b) y a / b se asigna a a.__truediv__(b), y así sucesivamente. Un punto a tener en cuenta, se invoca el método del objeto del lado izquierdo y pasa el objeto del lado derecho como parámetro. En el caso de x + y, se invoca el método __add__ de x y se pasa y como parámetro. Examinemos con un ejemplo.
Python3
class Count(object): def __init__(self, count): self._count = count def __add__(self, other): total_count = self._count + other._count return Count(total_count) def __str__(self): return 'Count: % i' % self._count c1 = Count(2) c2 = Count(5) c3 = c1 + c2 print(c3)
Producción
Count: 7
Método inverso
En el método Vanilla, el método del objeto del lado izquierdo se invoca al ejecutar un operador binario. Sin embargo, si el objeto del lado izquierdo no tiene un método para que el operador binario mapee, se llama al método inverso; comprueba el método del objeto del lado derecho para mapear. Echemos un vistazo al siguiente ejemplo:
Python3
class Count(object): def __init__(self, count): self._count = count def __add__(self, other): total_count = self._count + other._count return Count(total_count) def __radd__(self, other): if other == 0: return self else: return self.__add__(other) def __str__(self): return 'Count:% i' % self._count c2 = Count(2) c3 = 0 + c2 print(c3)
Producción
Count:2
Dado que 0 no tiene el método __add__ correspondiente, el intérprete de python llamaría al método __radd__ – c2.__radd__(0). De manera similar, si el método __sub__ no está definido, llamaría a __rsub __.
Método en el lugar
Tanto las operaciones de cálculo como las de asignación se realizan mientras se utilizan los métodos in situ. Algunos de los operadores que se asignan a métodos in situ son +=, -=, *=, etc. Los nombres de los métodos locales van precedidos de i. Por ejemplo, la instrucción x += y correspondería a x.__iadd__(y), y así sucesivamente. Veamos el siguiente ejemplo:
Python3
class inPlace(object): def __init__(self, value): self._value = value def __iadd__(self, other): self._value = self._value + other._value return self._value def __str__(object): return self._value inP1 = inPlace(5) inP2 = inPlace(3) inP1 += inP2 print(inP1)
Producción
8
Métodos mágicos para operadores unarios
- método __pos__
- método __neg__
- método __invertir__
método __pos__
El método __pos__ se invoca usando el operador +. Hemos visto que el operador + también funciona como un operador binario. No se preocupe, el intérprete de Python sabe cuál usar, unario o binario, según la situación. El método __pos__ toma un solo argumento posicional, self, realiza la operación y devuelve el resultado. Examinemos a través de un ejemplo:
Python3
class unaryOp(object): def __init__(self, value): self._value = value def __pos__(self): print('__pos__ magic method') return(+self._value) up = unaryOp(5) print(+up)
Producción
__pos__ magic method 5
método __neg__
El método __neg__ se llama usando el operador –. Este operador también actúa como un operador binario, pero según la situación, el intérprete determina qué método mágico mapear. El método mágico __neg__ acepta un solo argumento posicional, self, opera y devuelve el resultado. Veamos el siguiente ejemplo:
Python3
class unaryOp(object): def __init__(self, value): self._value = value def __neg__(self): print('__neg__ magic method') return(-self._value) up = unaryOp(5) print(-up)
Producción
__neg__ magic method -5
método __invertir__
El último operador unario es el método __invert__, que se invoca mediante el operador ~. La sentencia ~x es equivalente a x.__invert__(). Consideremos un ejemplo:
Python3
class invertClass(object): def __init__(self, value): self._value = value def __invert__(self): return self._value[::-1] def __str__(self): return self._value invrt = invertClass('Hello, George') invertedValue = ~invrt print(invertedValue)
Producción
egroeG, olleH
Algunos otros métodos mágicos
Analicemos algunos otros métodos mágicos:
- método __len__
- método __repr__
- __contiene__ método
Sobrecarga del método __len__
El método len() invoca el método mágico __len__. Toma un argumento posicional y devuelve la longitud del objeto. Veamos el siguiente código:
Python3
class RectangleClass(object): def __init__(self, area, breadth): self._area = area self._breadth = breadth def __len__(self): return int(self._area / self._breadth) rc = RectangleClass(90, 5) print(len(rc))
Producción
18
Importancia del método __repr__
El método mágico __repr__ ayuda a representar un objeto en la terminal interactiva de Python. Se necesita un argumento posicional: uno mismo. Veamos cómo se representa un objeto en la terminal interactiva de Python sin sobrecargar el método __repr__.
Python3
class RectangleClass(object): def __init__(self, area, breadth): self._area = area self._breadth = breadth def __len__(self): return int(self._area / self._breadth) ## use python interactive terminal to check object representation. RectangleClass(90, 5)
Producción
<__main__.RectangleClass object at 0x7f9ecaae9710>
Podemos ver, devuelve la dirección del objeto en la memoria, que no es tan útil. Veamos cómo podemos sobrecargar el método __repr__ para devolver una representación de objeto útil.
Python3
class RectangleClass(object): def __init__(self, area, breadth): self._area = area self._breadth = breadth def __len__(self): return int(self._area / self._breadth) def __repr__(self): """object representation""" return 'RectangleClass(area =% d, breadth =% d)' %\ (self._area, self._breadth) RectangleClass(90, 5) RectangleClass(80, 4)
Producción
RectangleClass(area=90, breadth=5) RectangleClass(area=80, breadth=4)
__contiene__ método mágico
El método __contains__ se llama cuando se ejecuta la expresión ‘in’. Toma dos argumentos posicionales, self y item, y devuelve verdadero si el elemento está presente o, de lo contrario, devuelve falso. Examinemos a través de un ejemplo:
Python3
import datetime class DateClass(object): def __init__(self, startDate, endDate): self.startDate = startDate self.endDate = endDate def __contains__(self, item): """ check whether a date is between the given range and return true or false""" return self.startDate <= item <= self.endDate dtObj = DateClass(datetime.date(2019, 1, 1), datetime.date(2021, 12, 31)) result = datetime.date(2020, 6, 4) in dtObj print("Whether (2020, 6, 4) is within the mentioned date range? ", result) result = datetime.date(2022, 8, 2) in dtObj print("Whether (2022, 8, 2) is within the mentioned date range? ", result)
Producción
Whether (2020, 6, 4) is within the mentioned date range? True Whether (2022, 8, 2) is within the mentioned date range? False
Resumen Por lo tanto, podemos concluir que los métodos mágicos son un modelo de datos consistente para personalizar el comportamiento de la clase y mejorar la legibilidad sin perder su característica heredada. Sin embargo, antes de dar una función personalizada, asegúrese de que la personalización sea necesaria o no.
Publicación traducida automáticamente
Artículo escrito por SonuGeorge y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA