Apariencia
📙 Clase 11 — Módulos e imports
Fase 2 · Funciones y código organizado ⬅️ Volver al índice de clases
🎯 Qué aprendí
- Partir el código en archivos (módulos) e importarlos:
import,from ... import. - El guardián
if __name__ == "__main__":y para qué sirve de verdad. - La estructura de carpetas de una app de escritorio real (la semilla de la Fase 6).
📖 PARTE TEÓRICA
📄 1. Un módulo = un archivo .py
Cuando app.py crece, se vuelve inmanejable. La solución: cada archivo .py es un módulo que puedes importar desde otro. Ya lo haces a diario:
python
import customtkinter as ctk # módulo de terceros (instalado con pip)
import json # módulo de la librería estándar
import calculos # ¡TU PROPIO archivo calculos.py!🧩 2. Crear e importar tu propio módulo
calculos.py — pura lógica, cero interfaz:
python
"""Módulo de lógica: NO sabe nada de la interfaz."""
IVA = 0.18
def con_iva(precio):
return round(precio * (1 + IVA), 2)
def es_email_valido(texto):
return "@" in texto and "." in textomain.py — lo consume:
python
import calculos # opción A: importar el módulo entero
from calculos import es_email_valido # opción B: traer solo lo que necesito
print(calculos.con_iva(50)) # 59.0 (con prefijo módulo.)
print(calculos.IVA) # 0.18 (también expone variables)
print(es_email_valido("ana@x.com")) # True (sin prefijo)| Forma | Cómo se usa luego | Cuándo conviene |
|---|---|---|
import modulo | modulo.funcion() | queda claro de dónde viene cada cosa |
from modulo import f | f() directo | usas pocas cosas y muy seguido |
import modulo as m | m.funcion() | nombres largos (customtkinter as ctk) |
from modulo import * | todo suelto | ⚠️ evítalo: no sabes qué trajo ni qué pisó |
🛡️ 3. if __name__ == "__main__":
Python le pone a cada módulo una variable __name__:
- Si ejecutas el archivo directo (
python calculos.py) →__name__vale"__main__". - Si lo importas desde otro → vale el nombre del módulo (
"calculos").
Eso permite tener código que corre solo en ejecución directa (pruebas, demo):
python
# al final de calculos.py
if __name__ == "__main__":
# Solo corre con: python calculos.py (NO al importarlo)
print("Probando el módulo:", con_iva(100)) # Probando el módulo: 118.0🧪 Tip de entrevista: "¿Qué hace
if __name__ == '__main__'?" → Distingue si el archivo se ejecuta directo o se importa. Protege el código de prueba/arranque para que no corra en cada import.
📌 Detalle práctico: los imports se cachean — si importas el mismo módulo dos veces, el archivo se ejecuta solo la primera.
🖥️ EN TU APP DE ESCRITORIO
Este es el paso de "script" a "proyecto". La estructura profesional mínima de una app CTk:
mi_app/
├── main.py 🚪 punto de entrada: crea la ventana y arranca el mainloop
├── ui.py 🎨 widgets, layouts, callbacks (lo ÚNICO que toca pantalla)
├── logica.py 🧠 validaciones y reglas (funciones puras con return)
└── datos.py 🗄️ leer/guardar (archivos hoy, SQLite en la Fase 6)python
# logica.py — testeable sin ventana
def validar_contacto(nombre, tel):
if not nombre:
return False, "El nombre está vacío"
if not tel.isdigit():
return False, "El teléfono debe ser numérico"
return True, ""python
# main.py
from ui import crear_app
if __name__ == "__main__": # el guardián SIEMPRE en el punto de entrada
app = crear_app()
app.mainloop()Beneficio inmediato: puedes probar tu lógica sin abrir la ventana:
bash
python -c "from logica import validar_contacto; print(validar_contacto('Ana','999'))"
# (True, '')💡 Regla de dependencias (memorízala):
uipuede importarlogicaydatos, perologicaydatosJAMÁS importanui. La lógica no sabe que existe una ventana. En la Fase 6 formalizamos esto como arquitectura MVC.
🗄️ CON BASE DE DATOS (caso de uso)
datos.py será el único archivo que hable con SQLite. Si mañana cambias SQLite por otra BD, solo tocas ese módulo:
python
# datos.py
import sqlite3
def conectar():
return sqlite3.connect("app.db")
def obtener_contactos(con):
return con.execute("SELECT nombre, tel FROM contactos").fetchall()🏋️ EJERCICIOS CON SOLUCIÓN
Ejercicio 1 — Tu primer módulo
Crea textos.py con una función titulo(s) que devuelva el texto capitalizado y sin espacios extra (" hola mundo " → "Hola Mundo"). Impórtala desde otro archivo y úsala.
Ver solución
python
# textos.py
def titulo(s):
return s.strip().title()
# main.py
from textos import titulo
print(titulo(" hola mundo ")) # Hola MundoEjercicio 2 — El guardián
Agrega a textos.py una prueba que solo corra al ejecutarlo directo. Comprueba las dos ejecuciones (python textos.py y desde el import).
Ver solución
python
# al final de textos.py
if __name__ == "__main__":
print(titulo(" probando el modulo ")) # solo con: python textos.py
# python textos.py → imprime "Probando El Modulo"
# python main.py → NO imprime la prueba (solo lo del main)Ejercicio 3 — Detecta el error de diseño
¿Qué está mal aquí (aunque "funcione")?
python
# logica.py
import ui
def validar(nombre):
if not nombre:
ui.label_error.configure(text="vacío")Ver solución
python
# La LÓGICA está importando la UI y tocando un widget: dependencia invertida.
# logica.py no debe saber que existe una pantalla. Correcto:
def validar(nombre):
if not nombre:
return False, "El nombre está vacío"
return True, ""
# ...y que ui.py reciba el (ok, msg) y decida cómo mostrarlo.❓ Preguntas y respuestas (autoevaluación)
1. ¿Qué es un módulo en Python?
Cualquier archivo
.py. Se importa conimport nombre(sin el.py).
2. ¿Cuándo vale "__main__" la variable __name__?
Cuando el archivo se ejecuta directo (
python archivo.py), no cuando se importa.
3. ¿Por qué evitar from modulo import *?
Trae todo sin saber qué: puede pisar nombres tuyos y no se ve de dónde salió cada función.
4. En una app GUI, ¿quién puede importar a quién?
uiimportalogica/datos; nunca al revés. La lógica no conoce la pantalla.
📎 Apuntes relacionados
- Las funciones puras que van en
logica.py→ Clase 08 · return - Comandos de venv/pip (de dónde salen los módulos de terceros) → 01-Comandos
➡️ Siguiente
Clase 12 · Excepciones — que tu app no se caiga: try/except.