Domina los decoradores en Python con un enfoque claro y práctico. Verás cómo proteger el acceso a funciones con una contraseña y cómo transformar resultados reutilizando lógica con un patrón profesional. La clave: entender el flujo con funciones anidadas, el uso de @ y el manejo de valores de retorno.
¿Cómo proteger una función con un decorador de contraseña?
Un decorador permite ejecutar lógica antes o después de una función. Aquí se usa para control de acceso con una constante de contraseña y el input del usuario.
¿Cuál es la estructura base del punto de entrada y la función?
Definir el punto de entrada con if __name__ == "__main__":.
Declarar una función a proteger que imprime un mensaje.
Usar una constante para la contraseña. En un entorno real se mencionó el hashing, pero aquí basta con una constante didáctica.
PASSWORD ="12345"defneeds_password():print("La contraseña es correcta.")defpassword_required(func):defwrapper(): pwd =input("¿Cuál es tu contraseña? ")if pwd == PASSWORD:return func()else:print("La contraseña no es correcta.")return wrapper
@password_requireddefneeds_password():print("La contraseña es correcta.")if __name__ =="__main__": needs_password()
¿Qué conceptos clave aplicar aquí?
Decorador: se declara con @ sobre la función objetivo.
Función anidada y scope: wrapper vive dentro de password_required.
Convención wrapper: la función interna suele llamarse así.
Indentación: el return wrapper pertenece a la función decoradora, no a wrapper.
Control de acceso: si la contraseña falla, la función protegida no se ejecuta.
¿Qué cambia cuando la función decorada recibe parámetros?
Cuando la función tiene parámetros, el decorador usa args y kwargs para no atarse a una firma específica. Además, si transformas el resultado (por ejemplo, con upper), la función decorada debe devolver un valor.
¿Cómo aplicar args y kwargs en el wrapper?
*args: captura argumentos posicionales.
**kwargs: captura argumentos con nombre.
La “expansión” con * y ** pasa los argumentos originales a la función decorada.
¿Cómo se prepara la función decorada para ser transformada?
Evitar print dentro de la lógica principal.
Usar return para que el decorador reciba el valor y pueda modificarlo.
@upperdefsay_my_name(nombre):returnf"Hola, {nombre}"if __name__ =="__main__":print(say_my_name("David"))# Hola, DAVID
¿Qué método de string se usa para la transformación?
upper: método de string que convierte el texto a mayúsculas.
Se aplica al resultado que regresa la función decorada.
¿Qué errores comunes y buenas prácticas resalta el instructor?
Comprender el flujo evita fallas típicas y hace más legible el código, una habilidad esencial cuando usas funciones de terceros la mayor parte del tiempo.
¿Por qué return importa más que print en decoradores?
Si la función decorada hace print y no return, su valor de retorno es None.
Llamar upper() sobre None produce un error: “'NoneType' object has no attribute 'upper'”.
Solución: devolver el valor y dejar el print en el punto de uso.
¿Cómo abordar mensajes de error y depuración?
Leer el mensaje e inferir el objeto y el método en conflicto.
Si el mensaje es críptico, buscar el texto del error en Google.
Verificar valores de retorno y flujos condicionales.
¿Qué otras buenas prácticas se muestran?
Usar constantes para configuraciones como contraseñas.
Cuidar la indentación al retornar el wrapper en el decorador.
Entender el punto de entrada con if __name__ == "__main__".
Reutilizar lógica con decoradores para seguridad y transformación.
Leer código como hábito: los decoradores aparecen en web, command line y data science.
Te leo en los comentarios: ¿qué patrón con decoradores usarás primero y qué caso práctico te gustaría automatizar con Python?
DECORADORES EN PYTHON
Los decoradores sirven para ejecutar lógica del código antes y/o después de otra función, esto nos ayuda a generar funciones y código que pueda ser reutilizado fácilmente sin hacer más extenso nuestro código. Hay que recordar que si se genera una función dentro de otra solo existiera en ese scope(dentro de la función padre), si se quiere invocar una función varias veces dentro de otras se tiene que generar de manera global.
**args y kwargs**
Básicamente lo que hacen es pasar tal cual los valores de de los argumentos que se pasan a la función args hace referencias a listas y kwargs a elementos de un diccionario (llave: valor)
** args: **
def test_valor_arg(n_arg,*args):print('primer valor normal: ', n_arg)For arg inargs:print('este es un valor de *args: ',arg)print(type(args))if__name__ =='__main__':test_valor_args('carlos','Karla','Paola','Elena')
el tipo de valor y es una tupla
solo poniendo argumentos divididos por comas los convierte
**kuargs: **
def test_valor_kwargs(**kwargs):if kwargs is not None:for key, value in kwargs.items():print('%s == %s'%(key,value))print(type(kwargs))if __name__ =='__main__':test_valor_kwargs(caricatura='batman')
el valor que te da es un diccionario
toma los valores en los extremos de un signo igual
Este es un ejemplo usando los 2 en una función
def test_valor_kwargs_args(*args,**kwargs):print(type(kwargs))print(kwargs)print('----------')print(type(args))print(args)if __name__ =='__main__':test_valor_kwargs_args('flash','batman', caricatura='batman', empresa ='dc')
Aquí hay un vídeo donde explican bien el tema de los decoradores: Decoradores - Codigo Facilito
Estos temas que cuestan un poco asimilarlos deberían ser explicados de la forma mas sencilla posible, ya teniendo claro el concepto se puede volver tan complejo como se requiera.
Interesante, ayuda y refuerza la clase.
Hice un micro juego de texto:
def upper(func): def wrapper(*args,**kwargs): result =func(*args,**kwargs)return result.upper()return wrapper
@upper
def say_my_name(name):if name =='Heisenberg':return'Youre Goddamn Right'else: name =input('Say my name! ')say_my_name(name)if __name__ =='__main__': name =input('Now, say my name ') result =say_my_name(name)print(result)
Me parece mucho más fácil entender los decoradores en Python que entender un callback en Javascript
x10000
Dentro del wrapper de password_required se puede omitir el else debido a que dentro del if se ejecuta una sentencia return haciendo que en caso de cumplirse la condición lo que esta después de ella nunca se ejecuta.
el else es para imprimir que es incorrecto el password
@jjjericu me refiero a la sentencia else, lo que está dentro de esta se mantiene solo que se arregla la identacion.
*args : le pasamos una cantidad indefinida de parámetros ( args es un nombre definido por convención, le puedes poner el que tu quieras ).
**kwargs : le pasamos una cantidad indefinida de parámetros, pero ahora con su respectiva clave ( o llave ), veamos un ejemplo:
def func(numero_1, numero_2):print(numero_1 + numero_2)func(numero_1 =5, numero_2 =3)# Vemos aqui que la llave es numero_1 y numero_2
Como vemos la sintaxis "numero_1 = 5" representa al parámetro con su respectiva clave, por eso se utiliza como convención "**kwargs" (te recuerdo que le puedes poner el nombre que quieras)
Aca el link que habla sobre *args y **kwargs me parecio sencillo de entender
Disculpen la mala redaccion jajaj, solo queria decir que la forma en que lo explicaba lo hacia de facil entendimiento, nada mas gente, saludos!
Hay que tener en cuenta 2 cosas:
1 si la funcion que estamos decorando recibe parametros, es necesario los *args y **kwargs, de lo contrario no es necesario.
2 se puede crear otra funcion que encierre nuestro decorador, para pasarle parametros al decorador, osea podemos a Los decoradores cambiar el comportamiento con parametros propios.
Este tema es bastante “blows my mind” Cuando se tenga un poco de experiencia y necesiten un método para “encapsular” la lógica del llamado a la API de las contraseñas en el backend lo van a entender mejor y van a saber de la importancia.
Por eso David en el min 3:02 dice que desde main o desde el punto de entrada no se puede llamar a la función wrapper() agregando así una capa de seguridad a nuestra lógica de manejo de login's. Como ven el método main no se llama en ningun momento la función wrapper que contiene la lógica para acceder.
Les dejare por separado los scripts que hizo el profe con pequeños cambios
aca el primero:
PASSWORD="hello123"def password_required(func): def wrapper(): password =input("What is the password? ")if password ==PASSWORD:returnfunc()else:print("The password is not correct")return wrapper
@password_required
def needs_password():print("The password is correct, welcome")if __name__ =="__main__":needs_password()
y aca el segundo:
your_name =input("What is your name? ")def upper(func): def wrapper(*args,**kwargs): result =func(*args,**kwargs)return result.upper()return wrapper
@upper
def say_your_name(name):return f"Hola, {name}"if __name__ =='__main__':print(say_your_name(your_name))
Consideren que los decoradores al retornar una función nueva esta no hereda las propiedades de la función original, para solucionar esto deben usar el decorador wraps del módulo functools, de esta manera las propiedades originales persisten.
from functools import wraps
defdecorador_sinwraps(func):defwrapper(*args,**kwargs):"""Main wrapper | Decorador"""return func(*args,**kwargs)return wrapper
defdecorador_conwraps(func):@wraps(func)defwrapper(*args,**kwargs):"""Main wrapper | Decorador"""return func(*args,**kwargs)return wrapper
deffunc1(valor:int):"""Función Quintuplicadora"""return valor *5@decorador_sinwrapsdeffunc2(valor:int):"""Función Duplicadora"""return valor *2@decorador_conwrapsdeffunc3(valor:int):"""Función Triplicadora"""return valor *2# Pruebas de persistencia de documentación.print(func1.__doc__)# Función Quintuplicadora# Se esperaría "Función Duplicadora" sin embargo resulta:print(func2.__doc__)# Main wrapper | Decorador# al usar wraps se "heredan" las propiedades de la función orginal:print(func3.__doc__)# Función Triplicadora
***args ** es para enviar tuplas como parametro a una función.
Podemos enviar esto:
func_args('A','B','C','D','E')
y recibirlo así:
def func_args(n_arg,*args):
n args contiene el 1er elemento ‘A’ *args contiene la tupla (‘B’, ‘C’, ‘D’, ‘E’)
**kwargs es para enviar diccionarios como parametros a una función.
Podemos enviar esto:
func_kwargs(caricatura ='batman', pelicula ='KillBill', serie ='BigBang')
y recibirlo así:
def func_kwargs(**kwargs):
**kwargs contiene el diccionario.
Aplicando los args a un ejercicio simple
"""
Definir una función max_de_tres(), que tome tres números como argumentos
y devuelva el mayor de ellos."""
def max_number(n_arg,*args):#print('Primer valor normal: ', n_arg)for i inrange(len(args)):if n_arg > args[i]: pass
else: n_arg = args[i]#print('Este es el valor de *args: ', args[i])return n_arg
def max_number_2(number1, number2, number3):if number1 > number2:if number1 > number3:return number1
else:return number3
else:if number2 > number3:return number2
else:return number3
if __name__ =='__main__': number1 =None number2 =None number3 =Nonewhile not number1: number1 =input('What is the first number? ')while not number2: number2 =input('What is the second number? ')while not number3: number3 =input('What is the third number? ')print('-'*30)print(max_number(number1, number2, number3))print('-'*30)print(max_number_2(number1, number2, number3))print('-'*30)print(max(number1, number2, number3)) # esta es la función max() que tiene incorporada python
Aquí dejo un ejemplo muy sencillo que usé para practicar decoradores. A pesar de lo sencillo me tomó bastante tiempo corregir la gran cantidad de errores que se prensentaron para completarlo, sobre todo al tratar de usar los **kwargs dentro del wrapper
"""Evalua si una persona esta en la lista de invitados"""import functools
amigo ={'name':'Alex','lastname':'Zambrano'}GUESTS=[{'name':'Jose','lastname':'Perez'},{'name':'Pedro','lastname':'Sanchez'}]def invitado(func): @functools.wraps(func) def wrapper(*args,**kwargs): found =Falsefor id,guest inenumerate(GUESTS):if guest['name']== kwargs['name'] and guest['lastname']== kwargs['lastname']:func(*args,**kwargs) found =Truebreakif found ==False:print('{} {} no se encuentra en la lista de invitados'.format(kwargs['name'],kwargs['lastname']))return wrapper
@invitado
def generate_identification_card(**kwargs):print('La identificacion para {} {} ha sido generada'.format(kwargs['name'],kwargs['lastname']))generate_identification_card(name=amigo['name'],lastname=amigo['lastname'])```
¡Hola @datalex! Te dejo este artículo muy bueno para complementar la explicación de args y kwargs de este curso ;)
Me dice el siguiente error despuEs de imprimir el resultado:
c:\PYTHON\PYTHON>python decoradores.pyPassword:12345Password correct
Traceback(most recent call last):File"decoradores.py", line 35,in<module>needs_password()TypeError:'NoneType' object is not callable