Saltar a contenido

Módulos y Paquetes

Los módulos y paquetes son formas de organizar el código en Python cuando los programas comienzan a tener un gran tamaño.

Módulos

Un módulo permite agrupar juntas funciones, clases y otro código en general. Esto resulta de utilidad cuando el proyecto comienza a ser grande o cuando existen partes del código que queremos reutilizar en varios proyectos.

Un módulo equivale a un fichero que contiene código en Python y puede contener:

  • Funciones
  • Clases
  • Variables
  • Código ejecutable
  • Atributos asociados al módulo como su nombre

El nombre del módulo es el nombre del fichero donde está definido (excluyendo la extensión ".py"). A continuación se muestra un ejemplo del código de un módulo contenido en el fichero utils.py.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""Esto es un módulo de prueba"""
print('Hola, soy el modulo de utilidades')

def imprimir(objeto):
    print('Imprimiendo')
    print(objeto)
    print('Fin')

class Forma:
    def __init__(self, id):
        self._id = id

    def __str__(self):
        return 'Forma: ' + self._id

    @property
    def id(self):
        """La documentacion de la propiedad id"""
        print('Ejecutando metodo id')
        return self._id

    @id.setter
    def id(self, value):
        print('Ejecutando metodo set_id')
        self._id = id

forma_basica = Forma('circulo') 

El módulo comienza en la línea 1 por un comentario que puede incluir información sobre la utilidad del mismo o sobre como usarlo.

En la línea 2 hay una instrucción con código ejecutable. El print mostrará un mensaje justo cuando el módulo sea cargado o inicializado por Python, es decir, la primera vez que se le referencie en la aplicación. Al cargar el módulo, en la línea 27 también se inicializará la variable forma_basica. Tanto esta variable como la función imprimir y la clase Forma se podrán utilizar desde el código que invoque al módulo.

Un módulo no es automáticamente accesible por otro código. Es necesario importar el módulo con la instrucción import. Al importarlo, todas las funciones, clases y variables contenidas en el módulo pasan a ser visibles en el código donde ha sido importado.

Para importar el módulo definido anteriormente será necesario escribir:

1
import utils

A partir de ese momento podemos utilizar las funciones, clases y variables contenidas en el módulo pero poniendo como prefijo el nombre del módulo de la siguiente forma:

1
2
3
4
5
import utils

utils.imprimir(utils.forma_basica)
f = utils.Forma('cuadrado')
utils.imprimir(f)

Resultado:

Hola, soy el modulo de utilidades
Imprimiendo
Forma: circulo
Fin
Imprimiendo
Forma: cuadrado
Fin

Se pueden importar varios módulos con una única intrucción import, separando sus nombre por comas:

1
import modulo1, modulo2, modulo3

Una convención es situar la importación de todos los módulos necesarios en un fichero del código al inicio del mismo. Aunque se puede colocar el import en cualquier lugar del fichero de código (siempre antes de utilizar los recursos contenidos en el módulo) intentaremos seguir esta convención en la medida de lo posible y ubicarlos al inicio.

Importar un módulo completo

Es posible importar todos los contenidos de un módulo evitando tener que escribir como prefijo el nombre del módulo cada vez que utilizamos uno de ellos con la siguiente instrucción:

1
2
3
4
5
from utils import *

imprimir(utils.forma_basica)
f = Forma('cuadrado')
imprimir(f)

El * implica importar todo el contenido del módulo y hacer sus nombres accesibles directamente.

Esto facilita el uso pero hay que tener mucha precaución con esta forma de importar un módulo porque los nombres de todas las variables, funciones y clases que tenga definidas el módulo pueden entrar en conflicto con los nombres que estemos utilizando en nuestro archivo de código (no pueden existir nombres repetidos).

Importar un elemento de un módulo

Una forma de mitigar este efecto es importar solamente aquel elemento que deseamos utilizar del módulo de la siguiente forma:

1
2
3
4
from utils import Forma

f = Forma('cuadrado')
print(f)

De esta forma importamos solamente la clase Forma y además evitamos tener que utilizar el prefijo del nombre del módulo a la hora de utilizarla.

Utilizar un alias

También es posible utilizar un alias tanto para un módulo completo como para un elemento del módulo. De esta forma nos podremos referir a ellos en nuestro código con ese alias.

Para importar un módulo con otro nombre utilizaremos la siguiente expresión:

1
2
3
import utils as ut

ut.imprimir(ut.forma_basica)

Para importar un elemento con otro nombre utilizaremos la siguiente expresión:

1
2
3
4
from utils import Forma, imprimir as show

f = Forma('cuadrado')
show(f)

En dicho ejemplo hemos cargado dos elementos del módulo utils: la clase Forma y la función imprimir y, a esta última, la hemos asignado un alias para poderla denominar show en nuestro código.

Ocultar elementos

Por defecto, cualquier elemento de un módulo cuyo nombre empiece por un guión bajo _ estará oculto cuando se importe todo el contenido del módulo utilizando un *. Solamente se podrán cargar estos elementos de forma individual y explícita.

Si tenemos el siguiente módulo en el fichero utils2.py.

1
2
3
4
"""Esto es un modulo con un elemento oculto"""

def _funcion_oculta():
    print('Funcion oculta')

Si cargamos el módulo con la siguiente instrucción:

1
from utils2 import *

no podremos acceder a la función _funcion_oculta.

En cambio, si cargamos el módulo con la siguiente intrucción:

1
2
3
from utils2 import _funcion_oculta

_funcion_oculta()

Resultado:

Funcion oculta

si es posible acceder a esa función.

Importar un módulo localmente

Es posible importar un módulo o un elemento de un módulo dentro de una función. En ese caso, los elementos del módulo solo serán accesibles desde dentro de esa función. Esta funcionalidad permite evitar conflictos de nombres cuando sea necesario.

1
2
3
def mi_funcion():
    from utils import Forma
    f = Forma('triangulo')

Propiedades de un módulo

Cada módulo tiene un conjunto de propiedades especiales que se pueden utilizar para obtener información sobre el módulo. Estas propiedades comienzan y finalizan por un doble guión bajo:

  • name el nombre del módulo.
  • doc el comentario descriptivo del módulo.
  • file el fichero en el cual está definido el módulo.

Además es posible obtener una lista de los contenidos del módulo mediante la función dir.

1
2
3
4
5
6
import utils

print(utils.__name__)
print(utils.__doc__)
print(utils.__file__)
print(dir(utils))

Resultado:

Hola, soy el modulo de utilidades
utils
Esto es un módulo de prueba
C:\Users\Miguel\Documents\codigo_python\utils.py
['Forma', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'forma_basica', 'imprimir']

Modulos estándar

Python incorpora múltiples módulos predefinidos.

Un módulo especialmente útil es el modulo sys que incorpora varias variables y funciones relacionadas con la plataforma donde se ejecuta el programa.

1
2
3
4
5
import sys

print('Version: ', sys.version)
print('Maxsize: ', sys.maxsize)
print('Platform: ', sys.platform)

Resultado:

Version:  3.8.4 (tags/v3.8.4:dfa645a, Jul 13 2020, 16:46:45) [MSC v.1924 64 bit (AMD64)]
Maxsize:  9223372036854775807
Platform:  win32

Otro elemento de utilidad dentro de sys es path que ofrece una lista de los directorios en los cuales se busca un módulo cuando se intenta cargar con un import.

1
2
3
import sys

print(sys.path)

Resultado:

['J:\\Web\\curso-python\\docs', 'C:\\Users\\Miguel\\AppData\\Local\\Programs\\Python\\Python38\\python38.zip', 'C:\\Users\\Miguel\\AppData\\Local\\Programs\\Python\\Python38\\DLLs', 'C:\\Users\\Miguel\\AppData\\Local\\Programs\\Python\\Python38\\lib', 'C:\\Users\\Miguel\\AppData\\Local\\Programs\\Python\\Python38', 'C:\\Users\\Miguel\\AppData\\Roaming\\Python\\Python38\\site-packages', 'C:\\Users\\Miguel\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages']