Día 12
Repaso
1. Objetos de función: nombre de la función
def funcion(): pass
1) f = funcion
2) def fun(funcion): pass
3) def func(): return funcion
4) dic = {'función': funcion} | lista = [funcion]
2. Espacios de nombres y ámbito de aplicación
LEGB
len("abc")
longitud = 10
del longitud
len("abc")
global palabra_clave L => G
numero = 100
def funcion():
global numero
numero = 10
def funcion1():
global
numero = 20
funcion1()
funcion()
print(numero)
3. Anidamiento de funciones y closures
def funcion1():
num = 10
def funcion2():
print(num)
def funcion1():
def funcion2():
def funcion3():
def funcion4():
pass
¿Qué es un closure: funcion2, función definida dentro de otra función
# Hoy
# Decoradores: una aplicación de los closures
Palabra clave nonlocal
# Propósito: Unificar los nombres del ámbito Local y Encerrado (el nombre en el ámbito Encerrado debe estar definido previamente)
# Caso de uso: Si se desea modificar el valor de una variable de función externa dentro de una función anidada
# Ejemplo:
def externa():
valor = 10
print(valor) # 10
def interna():
nonlocal valor
valor = 20
print(valor) # 20
interna()
print(valor) # 20
'''
# num = 0
# def fn():
# global num # L => G, unifica el nombre local con el nombre global
# num = 20
# fn()
# print(num)
def externa():
# global num
num = 0
def interna():
# Si se desea modificar el valor de la variable de función externa dentro de una función anidada
nonlocal num # Unifica el nombre del ámbito Local con el Encerrado (el nombre debe estar definido previamente en el ámbito Encerrado)
num = 10
print(num)
interna()
print(num)
externa()
# print(num)
'''
Principio de abierto-cerrado: no cambiar la forma de llamada ni aumentar funcionalidad en el código fuente
'''
1. No se puede modificar el código fuente del objeto (función) a decorar (cerrado)
2. No se puede cambiar la forma de llamar al objeto (función) decorado, y se debe lograr el efecto de aumentar la funcionalidad (abierto)
'''
# Versión 1.0
def funcion():
print('función ejecutada')
# funcion()
# Versión 2.0
# def funcion():
# print('función ejecutada0')
# print('función ejecutada1')
# print('función ejecutada2')
# funcion()
# Se modificó el código fuente, no se cambió la forma de llamada, la forma de llamada externa sigue siendo la misma, pero la funcionalidad debe aumentar (abierto)
# def funcion():
# print('función ejecutada0')
# print('función ejecutada')
# print('función ejecutada2')
# funcion()
# Se cambió la forma de llamada, no se modificó el código de la funcionalidad original (cerrado)
def contenedor(funcion):
print('función ejecutada0')
funcion()
print('función ejecutada2')
contenedor(funcion)
'''
1. No se puede modificar el código fuente del objeto (función) a decorar (cerrado)
2. No se puede cambiar la forma de llamar al objeto (función) decorado, y se debe lograr el efecto de aumentar la funcionalidad (abierto)
'''
Decroadores
# Pasa la función a decorar como parámetro a la función externa, luego a través de operaciones de closure devuelve una función de reemplazo
# Función a decorar: fn
# Función externa: exterior(func) exterior(fn) => func = fn
# Función de reemplazo: return interna: funcionalidad original + nueva funcionalidad
def funcion():
print("Funcionalidad original")
# Decorador
def decorador(etiqueta):
def interna():
etiqueta()
print("Funcionalidad agregada")
return interna
funcion = decorador(funcion)
funcion()
# Jarrón
# def jarron():
# print('Aflorar flores')
# jarron()
# Agregar una funcionalidad de pintar y luego admirar: no cumple con el principio abierto-cerrado, se modificó el código fuente
# def jarron():
# print('Aflorar flores')
# print('Pintar: para admirar')
# jarron()
# Agregar una funcionalidad de pintar y luego admirar: no cumple con el principio abierto-cerrado, se cambió la forma de llamada
# def contenedor(funcion):
# jarron()
# print('Pintar: para admirar')
# contenedor(jarron)
# Aunque se cumple con el principio abierto-cerrado, aparece un bucle infinito en la llamada a la función
# def funcion():
# jarron()
# print('Pintar: para admirar')
# jarron = funcion
# jarron()
# Conocimiento: cumple con el principio abierto-cerrado y puede lograr el efecto de decorador: expandir funcionalidad
# def jarron():
# print('Aflorar flores')
# etiqueta = jarron # Expuesto globalmente: fácil de modificar
# def funcion():
# etiqueta()
# print('Pintar: para admirar')
# jarron = funcion
# jarron()
def decorador(etiqueta):
def funcion():
etiqueta() # jarrón original
print('Pintar: para admirar')
return funcion # jarrón con funcionalidad expandida
jarron = decorador(jarron)
jarron() # Funcionalidad expandida, y la forma de llamada no cambia
Sintaxis de azúcar: @función_externa
def exterior(f):
def interna():
f()
print("Funcionalidad agregada1")
return interna
def contenedor(f):
def interna():
f()
print("Funcionalidad agregada2")
return interna
@contenedor # El orden de decoración determina el orden de ejecución de la nueva funcionalidad
@exterior # <=> fn = exterior(fn): interna
def fn():
print("Funcionalidad original")
def exterior(fn):
def interna():
fn()
print("Pintar: para admirar")
return interna
def contenedor(fn):
def interna():
fn()
print('Funcionalidad de exhibición')
return interna
# Sintaxis de azúcar | Sintaxis dulce
@contenedor
@exterior # <=> jarron = exterior(jarron)
def jarron():
print('Aflorar flores')
# jarron = exterior(jarron)
jarron()
# Resumen: una función puede ser decorada por cualquier decorador relacionado, o por varios decoradores
# Nota: El orden de decoración afecta el orden de ejecución de la nueva funcionalidad
Funciones con parámetros y retornno decoradas
def verificar_usuario(fn): # fn, login, interna: login en diferentes estados, por lo que los parámetros son uniformes
def interna(usuario, contrasena):
# Agregar nueva funcionalidad sobre la original
if not (len(usuario) >= 3 y usuario.isalpha()):
print('Validación de cuenta fallida')
return False
# Funcionalidad original
resultado = fn(usuario, contrasena)
# Agregar nueva funcionalidad debajo de la original
# ...
return resultado
return interna
@verificar_usuario
def login(usuario, contrasena):
if usuario == 'abc' y contrasena == '123qwe':
print('Inicio de sesión exitoso')
return True
print('Inicio de sesión fallido')
return False
# Resumen:
# 1. login tiene parámetros, por lo que interna y fn tienen los mismos parámetros
# 2. login tiene valor de retorno, por lo que interna y fn tienen valor de retorno
"""
interna(usuario, contrasena):
res = fn(usuario, contrasena) # Valor de retorno del login original
return res
login = verificar_usuario(login) = interna
res = login('abc', '123qwe') # Valor de retorno de interna
"""
# Versión original
# Agregar una funcionalidad de procesamiento de cuenta: 3 o más caracteres alfabéticos o en chino
def verificar_usuario(fn):
def interna(usuario, contrasena):
if not (len(usuario) >= 3 y usuario.isalpha()):
print('Validación de cuenta fallida')
return False
resultado = fn(usuario, contrasena) # login
return resultado
return interna
# Agregar una funcionalidad de procesamiento de contraseña: 6 o más caracteres alfabéticos y numéricos
def verificar_contrasena(fn):
def interna(usuario, contrasena):
if not (len(contrasena) >= 6 y contrasena.isalnum()):
print('Validación de contraseña fallida')
return False
return fn(usuario, contrasena)
return interna
# Funcionalidad de inicio de sesión
@verificar_usuario # login = verificar_usuario(login) = interna
@verificar_contrasena
def login(usuario, contrasena):
if usuario == 'abc' y contrasena == '123qwe':
print('Inicio de sesión exitoso')
return True
print('Inicio de sesión fallido')
return False
res = login('abc', '123qwe') # interna
print(res)
Escritura final de decoradores
def contenedor(fn):
def interna(*args, **kwargs):
print('Funcionalidad previa')
resultado = fn(*args, **kwargs)
print('Funcionalidad posterior')
return resultado
return interna
@contenedor
def funcion1():
print('Funcionalidad original de fn1')
@contenedor
def funcion2(a, b):
print('Funcionalidad original de fn2')
@contenedor
def funcion3():
print('Funcionalidad original de fn3')
return True
@contenedor
def funcion4(a, *, x):
print('Funcionalidad original de fn4')
return True
funcion1()
funcion2(10, 20)
funcion3()
funcion4(10, x=20)
Decoradores con parámetros: para conocimiento
# Para conocimiento
def exterior(color_entrada):
def contenedor(fn):
if color_entrada == 'rojo':
info = '\033[36;41mnueva acción\033[0m'
else:
info = 'amarillo:nueva acción'
def interna(*args, **kwargs):
pass
resultado = fn(*args, **kwargs)
print(info)
return resultado
return interna
return contenedor # exterior(color) => contenedor
color = input('color: ')
@exterior(color) # @exterior(color) => @contenedor # función => interna
def funcion():
print('funcion ejecutada')
funcion()
Funcionalidad de autenticación de inicio de sesión
estado_login = False # Estado de inicio de sesión
def login():
usuario = input('usuario: ')
if not (len(usuario) >= 3 y usuario.isalpha()):
print('Validación de cuenta fallida')
return False
contrasena = input('contraseña: ')
if usuario == 'abc' y contrasena == '123qwe':
print('Inicio de sesión exitoso')
estado_login = True
else:
print('Inicio de sesión fallido')
estado_login = False
# Completar un decorador de verificación de estado de inicio de sesión
def verificar_estado_login(fn):
def interna(*args, **kwargs):
# Antes de ver el perfil personal o la función de venta: si no se ha iniciado sesión, iniciar sesión primero, de lo contrario se puede acceder a su funcionalidad
if estado_login != True:
print('No has iniciado sesión')
login()
# Ver perfil personal o venta
resultado = fn(*args, **kwargs)
return resultado
return interna
# Funcionalidad de verificación de perfil personal
@verificar_estado_login
def perfil():
print('Perfil personal')
# Funcionalidad de venta
@verificar_estado_login
def vender():
print('Se puede vender')
perfil()