Modularización

Python al ser un lenguaje de programación multiparadigma permite trabajar tanto POO como programación estructurada. En lo que se refiere a la programación orientada a objetos (POO) hay que mencionar una propiedad que se conoce como modularidad o modularización.

La modularización en Python no es otra cosa que la posibilidad de la subdivisión del programa en partes mas pequeñas llamadas módulos los cuales son independientes del programa en sí. Estos módulos se encuentran en archivos que contienen código y cumplen una función en particular. Siendo posible la reutilización de estos módulos por otros programas. De hecho Python está estructurado de esta manera, contando con diferentes módulos que pueden ser usados en el código que programemos en nuestros scripts. De hecho en otros apartados ya hemos usado algunos módulos de Python tales como SYS y OS, a través del comando IMPORT.

Los módulos se guardan en archivos de texto con extensión .py o .pyc, también puede ser un arcchivo escrito en lenguaje C para cPython. Estos archivos o módulos tienen su propio espacio de nombres pudiendo contener sus propias variables, funciones, clases e incluso contener otros módulos.

Para ilustrar mejor lo anterior vamos a crear una carpeta que contenga todos los módulos o scripts que vamos a utilizar. En este ejemplo a vamos a llamar módulos y en esta carpeta vamos a crear 2 archivos .py, uno llamado principal.py y el otro matematicas.py. En el archivo matematicas.py simplemente vamos a crear 2 funciones lambdas llamadas sumar y restar para sumar y restar 2 elementos recibidos como argumentos. En el archivo principal.py importaremos las funciones sumar y restar creadas en el otro archivo con el uso del comando FROM <nombre del archivo .py> IMPORT <función a importar>. Hay que tomar en cuenta que se pueden importar varias funciones en una sola instrucción separándolas con coma o colocando el símbolo asterisco (*). Luego dentro de la función principal en el archivo principal.py imprimiremos por pantalla con el comando PRINT() los objetos sumar y restar con los argumentos respectivos. Adicionalmente en el módulo matematicas.py vamos a agregar al final 2 instrucciones PRINT() con los objetos sumar y restar que contienen las funciones lambdas con argumentos de prueba para verificar el funcionamiento del módulo matematicas.py antes de ser utilizado en otro módulo. Veamos el contenido del archivo matematicas.py:

sumar = lambda a,b:a+b
restar= lambda a,b:a-b

print(sumar(4,4))
print(restar(5,3))

Resultado:

E:\Documentos\Desarrollos\Python\Curso-Python>"C:/Program Files/Python310/python.exe" e:/Documentos/Desarrollos/Python/Curso-Python/modulos/matematicas.py

8

2


Como podemos ver, al imprimir los objetos sumar y restar con los argumentos de prueba 4,4 y 5,3 respectivamente, el resultado es efectivamente la suma de los dos primeros argumentos en la función sumar y la resta de los dos últimos argumentos en la función restar, comprobando que las funciones del módulo matemaricas.py funcionan correctamente. Ahora bien podemos hacer uso de estas funciones desde otro módulo como se explicó anteriormente. Veamos el contenido del módulo principal.py:

from matematicas import sumar,restar

def principal():
    print(sumar(3,4))
    print(restar(23,2))

principal()

al ejecutar el código el resultado es:

E:\Documentos\Desarrollos\Python\Curso-Python>"C:/Program Files/Python310/python.exe" e:/Documentos/Desarrollos/Python/Curso-Python/modulos/principal.py

8

2

7

21


Vemos como el intérprete de Python ha hecho uso de las funciones localizadas en el módulo matematicas.py, al sumar y restar correctamente los valores pasados como argumentos desde la función principal() localizada en el módulo principal.py. Sin embargo también ha ejecutado las instrucciones que se hicieron de pruebas dentro del módulo. Si bien en un ejemplo tan sencillo como este bastaría con eliminar las instrucciones de prueba del módulo matematicas.py, es posible que en un escenario mas complejo sea necesario mantener el código del módulo importado para cuando se desee ejecutar el módulo de manera independiente, pero en caso de ser importado desde otro módulo omita cierta parte del código o viceversa. Para ello haremos uso de los puntos de entrada.

Hay que mencionar que el intérprete de Python le asigna a la variable __name__ el valor '__main__' cuando se ejecuta el script directamente, o sea cuando no esta siendo llamado desde otro módulo, sin embargo, si el script es importado desde otro módulo, Python le asigna a esta variable el nombre del script que lo contiene, en nuestro ejemplo le asignaria el valor ''matematicas".

Sabiendo esto, bastaría con colocar una instrucción IF que verifique el contenido de la variable especial __name__ en el módulo matematicas.py, y solamente ejecutar las instrucciones de prueba en caso que el contenido de la variable __name__ sea ''__main__". Como regla general es una buena práctica de programación en Python encerrar dentro del punto de entrada la función principal de la siguiente manera:

if __name__=='__main__':
    principal()

De esta forma nos aseguramos que se ejecute la función principal únicamente si se está ejecutando el script directamente y no esta siendo llamado desde otro módulo para acceder a sus otras funciones.

El código final del módulo matematicas.py quedaría de la siguiente manera:

sumar = lambda a,b:a+b
restar= lambda a,b:a-b
if __name__=='__main__':
    print(sumar(4,4))
    print(restar(5,3))

mientras que el código final del módulo principal.py quedaría así:

from matematicas import sumar,restar

def principal():
    print(sumar(3,4))
    print(restar(23,2))

if __name__=='__main__':
    principal()

Resultado de la ejecución del módulo matematicas.py directamente:

E:\Documentos\Desarrollos\Python\Curso-Python>"C:/Program Files/Python310/python.exe" e:/Documentos/Desarrollos/Python/Curso-Python/modulos/matematicas.py

8

2


Resultado de la ejecución del módulo principal.py:

E:\Documentos\Desarrollos\Python\Curso-Python>"C:/Program Files/Python310/python.exe" e:/Documentos/Desarrollos/Python/Curso-Python/modulos/principal.py

7

21

Como podemos comprobar el sistema de modularización en este sencillo ejemplo esa funcionando correctamente. Para terminar este apartade de modularización vamos a comprobar que efectivamente Python importe todas las funciones de un módulo usando el símbolo asterisco (*):

from matematicas import *

def principal():
    print(sumar(3,4))
    print(restar(23,2))

if __name__=='__main__':
    principal()

Resultado:

7

21

Efectivamente ha funcionado como se esperaba. Es importante destacar que usando este método de modularización (FROM <nombre del archivo> IMPORT <modulos a importat separados por coma>) equivale a escribir los módulos importados directamente en el archivo que contiene la instrucción IMPORT, pudiendo invocar a las funciones dentro del programa principal como si las funciones estuvieran escritas dentro de él. Sin embargo existe otra manera de usar la modularización, importando el módulo completo, para hacerlo simplemente se omite el comando FROM, usando directamente la instrucción IMPORT seguido del nombre del archivo o módulo que contiene el código que se desea importar. En este caso hay que escribir el nombre del módulo importado seguido de punto y el nombre de la función a utuizar, ya que en caso contrario no funcionaría. Veamos el ejemplo anterior usando el método antes descrito:

import matematicas
def main():
    print(mate.sumar(5,4))
    print(mate.restar(10,5))

 El código del archivo matematicas.py se mantiene igual:

sumar =  lambda a,b:a+b
restar = lambda a,b:a-b

if __name__=='__main__':
    print(sumar(4,4))
    print(restar(5,3))

Resultado:

9

5

Se puede apreciar que el resultado obtenido es el mismo con ambas maneras de plantear el uso de la modularización. 


Paquetes

En Python la organización de diferentes módulos o archivos .py dentro de una carpeta común para el mismo proyecto es lo que se conoce como paquete. Es una manera de organizar un proyecto un poco mas grande y mas complejo que un par de scripts con pocas funciones, como habíamos visto hasta ahora. Para empezar a crear paquetes en Python, basta con crear una carpeta que contenga los archivos del proyecto y dentro de esa carpeta crear un archivo vacío que tenga el nombre __init__.py. Posteriormente Python reconocerá el contenido de dicha carpeta como un paquete y permitirá hacer referencia a los scripts creados allí a través del operador punto (.) como objetos pertenecientes a un paquete principal con el nombre de la carpeta contenedora.

Para ilustrar con un ejemplo sencillo vamos a crear un paquete (carpeta) llamado paquete que contenga un script llamado figuras.py con una clase llamada cuadrado que a su vez tenga una propiedad llamada lado y 2 métodos. Un método que devuelva el área del cuadrado multiplicando el lado por si mismo y otro método que devuelva el perímetro multiplicando el valor del lado por 4 (lo que equivale a sumar el lado de un cuadrado 4 veces). De igual manera el paquete va a contener obligatoriamente su archivo vacío  __init__.py para que Python reconozca la carpeta como un paquete y permita la importación de sus scripts y el acceso a sus objetos. Desde el módulo principal, donde venimos trabajando vamos a importar los scripts accesando a este nuevo paquete y a instanciar la clase cuadrado en un objeto llamado c1 definiendo en la clase del objeto un valor de 8 unidades como argumento del parámetro lado, para finalizar el script principal.py mostrando por pantalla los valores devueltos por los métodos area() y perimetro() del objeto creado c1. Para hacer la importación del script figuras del paquete llamado "paquete" nos valdremos de la ya conocida instrucción FROM indicando que vamos a importar la clase "cuadrado" pero bajo un alias que será cuad. No es que esto sea necesario pero para este ejemplo lo vamos a hacer así para ver su uso y sintaxis.

Contenido del script figuras.py ubicado dentro de la carpeta "paquete":

class cuadrado:
    def __init__(self,lado) -> None:
        self.lado=lado
   
    def area(self):
        return self.lado*self.lado
   
    def perimetro(self):
        return self.lado*4

Contenido del script principal.py ubicado en la raíz del proyecto:

from matematicas import *
from paquete.figuras import cuadrado as cuad

def principal():
    print(sumar(3,4))
    print(restar(23,2))

    c1 = cuad(8)
    print(f'El area del cuadrado c1 de 8 unidades de lado es de: {c1.area()}')
    print(f'El perímetro del cuadrado c1 de 8 unidades de lado es de: {c1.perimetro()}')


if __name__=='__main__':
    principal()

Resultado de la ejecución del script principal.py:

7

21

El area del cuadrado c1 de 8 unidades de lado es de: 64

El perímetro del cuadrado c1 de 8 unidades de lado es de: 32


Como podemos ver el sistema de paquetes esta funcionando como era de esperarse.





Comentarios

Entradas populares