Python provee un conjunto de métodos que son muy útiles
Dunder methods:
__init__()
nos permite inicializar una clase.
__call__()
permite que nuestras instances sean callables (funciones)
class fun:
def __call__(self, x):
return x + 25
g = fun()
g(5)
class add_m:
def __call__(self,x,y):
return x + y
g = add_m()
g(5,5)
Hay dos magic methods muy utilizados:
__repr__
: para la imprimir instancias: unambiguous representation of an object
__str__
: readable representation of an object
Rule of the thumb: cuando escribamos un __repr__
la salida tiene que ser tal que con un copy and past podemos recrear de manera no ambigua dicho objeto
Miremos que sucede antes de introducir __repr__
si hacemos print(instance de Empleados)
class Empleados:
tasa_incremento_salarial = 1.1
num_de_emps = 0
def __init__(self, nombre, apellido, salario):
self.nombre = nombre
self.apellido = apellido
self.salario = salario
self.email = nombre + "." + apellido + "@emp.com"
Empleados.num_de_emps += 1
def nombre_completo(self):
print(f"{self.nombre} {self.apellido}")
def incremento_salarial(self):
self.salario = int(self.salario * self.tasa_incremento_salarial)
emp_1 = Empleados("Daniel", "Miles", 100000)
print(emp_1)
La representación es ambigua, no queda clara: es un objeto de empleados en una dirección de memoria
Introduzcamos el método __repr__()
con el objetivo de tener una representación no ambigua de la instancia.
class Empleados:
tasa_incremento_salarial = 1.1
num_de_emps = 0
def __init__(self, nombre, apellido, salario):
self.nombre = nombre
self.apellido = apellido
self.salario = salario
self.email = nombre + "." + apellido + "@emp.com"
Empleados.num_de_emps += 1
def nombre_completo(self):
print(f"{self.nombre} {self.apellido}")
def incremento_salarial(self):
self.salario = int(self.salario * self.tasa_incremento_salarial)
def __repr__(self):
return f'Empleados("{self.nombre}", "{self.apellido}", {self.salario})'
emp_1 = Empleados("Daniel", "Miles", 100000)
print(emp_1)
El __str__()
esta pensado para la presentación al usuario, por lo que es más arbitrario.
Cuando no está implementado, y lo llamamos, recurre a __repr__()
emp_1.__str__()
class Empleados:
tasa_incremento_salarial = 1.1
num_de_emps = 0
def __init__(self, nombre, apellido, salario):
self.nombre = nombre
self.apellido = apellido
self.salario = salario
self.email = nombre + "." + apellido + "@emp.com"
Empleados.num_de_emps += 1
def nombre_completo(self):
return f"{self.nombre} {self.apellido}"
def incremento_salarial(self):
self.salario = int(self.salario * self.tasa_incremento_salarial)
def __repr__(self):
return f'Empleados("{self.nombre}", "{self.apellido}", {self.salario})'
def __str__(self):
return f'Empleado: {self.nombre_completo()} e.mail: {self.email} Salario: {self.salario}'
emp_1 = Empleados("Daniel", "Miles", 100000)
print(emp_1)
repr(emp_1)
str(emp_1)
Existen varios special methods
Ver en el punto 3.3 en Python Documetnation
print(int.__add__(5,4))
print(str.__add__("5","4"))
Podemos utilizar, entonces, otros dunder methods para automatizar nuestra clase.
Imaginemos que queremos sumar el salario de dos trabajadores.
class Empleados:
tasa_incremento_salarial = 1.1
num_de_emps = 0
def __init__(self, nombre, apellido, salario):
self.nombre = nombre
self.apellido = apellido
self.salario = salario
self.email = nombre + "." + apellido + "@emp.com"
Empleados.num_de_emps += 1
def nombre_completo(self):
return f"{self.nombre} {self.apellido}"
def incremento_salarial(self):
self.salario = int(self.salario * self.tasa_incremento_salarial)
def __repr__(self):
return f'Empleados("{self.nombre}", "{self.apellido}", {self.salario})'
def __str__(self):
return f'Empleado: {self.nombre_completo()} e.mail: {self.email} Salario: {self.salario}'
def __add__(self, other):
return self.salario + other.salario
emp_1 = Empleados("Daniel", "Miles", 100000)
emp_2 = Empleados("Ines", "Gonzalez", 150000)
emp_1 + emp_2
Veamos otro ejemplo: len
mide el numero de elementos de una secuencia
len(emp_1.nombre_completo())
emp_1.nombre_completo().__len__()
class Empleados:
tasa_incremento_salarial = 1.1
num_de_emps = 0
def __init__(self, nombre, apellido, salario):
self.nombre = nombre
self.apellido = apellido
self.salario = salario
self.email = nombre + "." + apellido + "@emp.com"
Empleados.num_de_emps += 1
def nombre_completo(self):
return f"{self.nombre} {self.apellido}"
def incremento_salarial(self):
self.salario = int(self.salario * self.tasa_incremento_salarial)
def __repr__(self):
return f'Empleados("{self.nombre}", "{self.apellido}", {self.salario})'
def __str__(self):
return f'Empleado: {self.nombre_completo()} e.mail: {self.email} Salario: {self.salario}'
def __add__(self, other):
return self.salario + other.salario
def __len__(self):
return len(self.nombre_completo())
emp_2 = Empleados("Ines", "Gonzalez", 150000)
len(emp_2)
La idea es incorporar estos métodos a la clase para ganar funcionalidad, e.g., comparar, iterar, etc...
Dado lo que hemos visto hasta ahora, vamos a mirar ahora iterables e iteradores.
Ejemplo: iterador
class PowTwo:
"""Class para implementar un iterador de
powers of two"""
def __init__(self, max=0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration