102 - Funciones anidadas

El lenguaje Python permite definir una función dentro de otra función.

Cuando necesitamos una pequeña "función" que solo tiene sentido dentro del contexto de otra función, es aquí donde entran en juego las funciones anidadas.

Problema 1

Definir una función interna a otra función.

Programa: ejercicio346.py

def funcion_principal():

    def funcion_interna():
        print("Hola desde la función interna")

    funcion_interna()
    print("Hola desde la función principal")

#bloque principal
funcion_principal()

Simplemente declaramos la función 'funcion_interna' dentro de la declaración de la 'funcion_principal'. La visibilidad de la 'funcion_interna' solo es conocida por la 'funcion_principal'. No podemos invocar a la 'funcion_interna' desde el bloque principal de nuestro programa.

#bloque principal
funcion_principal()
funcion_interna()  # Esto causará un error porque la función interna no es accesible aquí.

Problema 2

Crear una función que reciba una clave y retorne True si tiene al menos un número, una letra minúscula y una letra mayúscula. Definir 3 funciones anidadas que controlen cada uno de los tres casos.

Programa: ejercicio347.py

def validar_clave(clave):
    def tiene_minuscula():
        for caracter in clave:
            if caracter.islower():
                return True
        return False

    def tiene_mayuscula():
        for caracter in clave:
            if caracter.isupper():
                return True
        return False

    def tiene_numero():
        for caracter in clave:
            if caracter.isdigit():
                return True
        return False

    return tiene_minuscula() and tiene_mayuscula() and tiene_numero()


# --- Ejemplos de uso ---
print("--- Clave Válida ---")
clave1 = "MiClaveSegura123"
print(f"'{clave1}' es válida: {validar_clave(clave1)}") # Esperado: True
print("-" * 30)

print("\n--- Clave sin mayúscula ---")
clave2 = "miclavesegura123"
print(f"'{clave2}' es válida: {validar_clave(clave2)}") # Esperado: False
print("-" * 30)

print("\n--- Clave sin minúscula ---")
clave3 = "MICLAVESEGURA123"
print(f"'{clave3}' es válida: {validar_clave(clave3)}") # Esperado: False
print("-" * 30)

print("\n--- Clave sin número ---")
clave4 = "MiClaveSegura"
print(f"'{clave4}' es válida: {validar_clave(clave4)}") # Esperado: False
print("-" * 30)

print("\n--- Clave con solo minúsculas y números ---")
clave5 = "miclave123"
print(f"'{clave5}' es válida: {validar_clave(clave5)}") # Esperado: False
print("-" * 30)

print("\n--- Clave vacía ---")
clave6 = ""
print(f"'{clave6}' es válida: {validar_clave(clave6)}") # Esperado: False
print("-" * 30)

Las 3 funciones internas 'tiene_minuscula', 'tiene_mayuscula' y 'tiene_numero' solo pueden ser invocadas desde dentro de la función 'validar_clave'.

Cada función anidada es una pequeña unidad de lógica que está intrínsecamente ligada a la validación de una clave dentro del contexto de 'validar_clave' y no tienen un propósito útil fuera de esta función, es por ello que se las dispone en forma local a la función principal.

Las funciones anidadas pueden acceder a los parámetros de la función principal y a sus variables locales, es decir en nuestro problema la función 'tiene_minuscula' puede acceder al parámetro llamado 'clave' definido en la función 'validar_clave'. Este acceso es una característica fundamental de las funciones anidadas.

Las funciones anidadas son útiles para organizar y modularizar el código dentro de una función más grande, manteniendo la limpieza y evitando la "contaminación" del espacio de nombres global.

Como ha quedado expuesto, la declaración de una función anidada es sorprendentemente sencilla y sigue la misma sintaxis que cualquier otra función en Python, con una única pero crucial diferencia: su definición ocurre dentro del cuerpo de otra función o método de una clase.