98 - Módulos y paquetes en Python

Vimos en conceptos anteriores que podemos dividir una aplicación en varios módulos y luego importarlos.

Recordemos que un módulo es un archivo con extensión 'py' y contiene en su interior funciones, clases, definición de variables etc.

Veremos ahora que un paquete en Python agrupa un conjunto de módulos relacionados y se los localiza en un directorio.

Veremos con un sencillo problema como creamos un paquete en Python.

Problema:

Ver video

Desarrollar una aplicación visual que muestre dos botones. Al presionar el primero mostrar otra ventana para el login, y al presionar el segundo mostrar una ventana de mensajes.

Crear un paquete llamado 'formularios' que contenga en su interior dos módulos llamados 'login.py' y 'mensaje.py'.

La aplicación principal llamarla 'principal.py'

Debemos crear una carpeta llamada 'formularios' y en su interior tres archivos: 'login.py', 'mensaje.py' y '__init__.py'.

El archivo '__init__.py' generalmente se encuentra vacío y tiene por objetivo indicar al intérprete de Python que dicha carpeta es un paquete.

Es decir que debemos tener los siguientes archivos y carpeta en nuestra aplicación:

módulos y paquetes en python

módulo: login.py

import tkinter as tk
from tkinter import ttk

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        self.labelframe1=ttk.LabelFrame(self.ventana1, text="Login:")        
        self.labelframe1.grid(column=0, row=0, padx=5, pady=10)        
        self.login()

    def login(self):
        self.label1=ttk.Label(self.labelframe1, text="Nombre de usuario:")
        self.label1.grid(column=0, row=0, padx=4, pady=4)
        self.entry1=ttk.Entry(self.labelframe1)
        self.entry1.grid(column=1, row=0, padx=4, pady=4)
        self.label2=ttk.Label(self.labelframe1, text="Ingrese clave:")        
        self.label2.grid(column=0, row=1, padx=4, pady=4)
        self.entry2=ttk.Entry(self.labelframe1, show="*")
        self.entry2.grid(column=1, row=1, padx=4, pady=4)
        self.boton1=ttk.Button(self.labelframe1, text="Ingresar",command=self.ingresar)
        self.boton1.grid(column=1, row=2, padx=4, pady=4)

    def ingresar(self):
        self.ventana1.destroy()
        

def mostrar():
    aplicacion1=Aplicacion()

módulo: mensaje.py

from tkinter import messagebox as mb

def mostrar(mensaje):
    mb.showerror("Cuidado",mensaje)        

Recordar de crear el archivo '__init__.py' en la carpeta 'formularios' y sin contenido (luego veremos que podemos disponer código en su interior que se ejecutará cuando importemos algún módulo del paquete)

Por otro lado creamos en la carpeta superior el script que contendrá la aplicación principal de nuestro programa que lo llamaremos 'principal.py'

principal.py

import tkinter as tk
from tkinter import ttk

import formularios.login
import formularios.mensaje

class Aplicacion:
    def __init__(self):
        self.ventana1=tk.Tk()
        self.ventana1.title("Hola Mundo")        
        self.boton1=ttk.Button(self.ventana1, text="Mostrar Mensaje", command=self.mostrar_mensaje)
        self.boton1.grid(column=0, row=0)
        self.boton2=ttk.Button(self.ventana1, text="Mostrar formulario de login", command=self.mostrar_login)
        self.boton2.grid(column=0, row=1)
        self.ventana1.mainloop()

    def mostrar_mensaje(self):
        formularios.mensaje.mostrar("Es una prueba de acceder a módulos de un paquete")

    def mostrar_login(self):
        formularios.login.mostrar()

aplicacion1=Aplicacion()

Si ejecutamos la aplicación desde el script 'principal.py' tenemos como resultado en pantalla al presionar el botón "Mostrar formulario de login":

módulos y paquetes en python

Como vemos la sintaxis para importar un módulo contenido en un paquete debemos anteceder al nombre del módulo el nombre del paquete:

import formularios.login
import formularios.mensaje

Luego podemos acceder al módulo respectivo indicando primero el nombre del paquete, seguidamente el nombre del módulo y finalmente el nombre de la función:

        formularios.login.mostrar()

De forma similar llamamos a la función 'mostrar' del módulo 'mensaje' contenido en el paquete 'formularios':

        formularios.mensaje.mostrar("Es una prueba de acceder a módulos de un paquete")

Acotaciones.

  1. Recordemos que podemos crear un alias cuando importamos un módulo con el fin de escribir menos cada vez que lo accedemos:

    import tkinter as tk
    from tkinter import ttk
    
    import formularios.login as formlogin
    import formularios.mensaje as formmensaje
    
    class Aplicacion:
        def __init__(self):
            self.ventana1=tk.Tk()
            self.ventana1.title("Hola Mundo")        
            self.boton1=ttk.Button(self.ventana1, text="Mostrar Mensaje", command=self.mostrar_mensaje)
            self.boton1.grid(column=0, row=0)
            self.boton2=ttk.Button(self.ventana1, text="Mostrar formulario de login", command=self.mostrar_login)
            self.boton2.grid(column=0, row=1)
            self.ventana1.mainloop()
    
        def mostrar_mensaje(self):
            formmensaje.mostrar("Es una prueba de acceder a módulos de un paquete")
    
        def mostrar_login(self):
            formlogin.mostrar()
    
    aplicacion1=Aplicacion()
    

    Como vemos llamamos a la función mostrar del módulo 'login' directamente antecediendo el nombre del alias creado:

            formlogin.mostrar()
    
  2. También podemos hacer uso de la sintaxis del 'from' para importar módulos de un determinado paquete:

    import tkinter as tk
    from tkinter import ttk
    
    from formularios import login
    from formularios import mensaje
    
    class Aplicacion:
        def __init__(self):
            self.ventana1=tk.Tk()
            self.ventana1.title("Hola Mundo")        
            self.boton1=ttk.Button(self.ventana1, text="Mostrar Mensaje", command=self.mostrar_mensaje)
            self.boton1.grid(column=0, row=0)
            self.boton2=ttk.Button(self.ventana1, text="Mostrar formulario de login", command=self.mostrar_login)
            self.boton2.grid(column=0, row=1)
            self.ventana1.mainloop()
    
        def mostrar_mensaje(self):
            mensaje.mostrar("Es una prueba de acceder a módulos de un paquete")
    
        def mostrar_login(self):
            login.mostrar()
    
    aplicacion1=Aplicacion()
    
  3. Un paquete en Python tiene en su interior módulos, pero puede tener otros subpaquetes que se deben crear en forma similar al paquete padre. También el subpaquete además de los módulos debe tener el archivo '__init__.py'.

    Cuando importamos un subpaquete debemos indicar el nombre de la carpeta del paquete principal, luego el nombre de la carpeta del subpaquete y finalmente el nombre del módulo:

    import formularios.modales.login
    import formularios.modales.mensaje
    import formularios.nomodales.ayuda
    

    El nivel de subcarpetas dependerá de la complejidad de la aplicación que estemos implementando.