Importar módulos Python



@eduardo_gpg

Número de visitas 10629

Tiempo de lectura 4 min

15 Febrero 2022

En una entrega anterior hablamos sobre módulos y paquetes en Python (si aun no has leído el post de invito a que lo hagas, no te arrepentirás). Pues bien, como complemento a dicho contenido, en esta nueva entrega me gustaría hablar sobre las diferentes formas en las que podemos importar de módulos y paquetes en Python. Es algo muy interesante, y que sin duda, en muchas ocasiones, pasamos por alto. 😎

Bien, sin más introducción, comencemos con el tutorial. 🔥

Imports

Lo primero que debemos tener en cuenta es que, en Python, nosotros podemos importar objetos mediante las palabras reservada from e import. Podremos importar de módulos y paquetes los objetos que deseemos, desde variables, contantes, pasando por funciones, clases hasta módulos y paquetes.

Veamos.

Para este post estaremos trabajando con el siguiente paquete (entities).

Directorio

root/

├── entities/
│   ├── __init__.py
│   ├── user.py

└── main.py

user.py

class User():
    def __init__(self, username, password):
        self.username = username
        self.password = password


    def __str__(self):
        return self.username

Si nosotros quisiéramos importar las clases User, podemos hacer lo siguiente.

main.py

from entities.user import User

En este caso le indicamos a Python, mediante la palabra reservada from, el origen del objeto que vamos importar y posteriormente, mediante la palabra reservada import, el objeto a importar. 🐍

Si quisiéramos importar directamente del paquete, sin pasar por el módulo user, modificamos el archivo __init__.py.

__init__.py.

from .user import User

En este caso utilizamos punto (.) para denotar que el módulo se encuentra dentro del paquete.

O podemos hacerlo de forma explicita.

from entities.user import User

De ambas formas obtendremos el mismo resultado. Aunque en lo personal he visto más el uso de punto (.). 😅

Listo, con este ligero cambio el import en main quedaría de la siguiente manera.

from entities import User

De igual forma, definimos el origen del objeto y el objeto mismo a importar.

Ahora, qué pasa si nosotros deseamos importar una gran cantidad de objetos de un mismo origen (paquete o módulo). Bueno, en esos casos tenemos un par de formas de hacerlo.

La primera, y la que siempre recomiendo, es importar, línea por línea, cada uno de los objetos que necesitemos.

Ejemplo.

main.py

from entities import User
from entities import Seller
from entities import Customer
from entities import Admin

Sí, sé que esto puede consumir una gran cantidad de líneas de código y puede ser algo tedioso de realizar, pero nos estaremos asegurando de importar, única y exclusivamente lo que necesitamos, además que el código será mucho más legible.

Otra forma de obtener el mismo resultado es realizar el import en una sola línea. 🧐

from entities import Seller, Customer, Admin

Este opción no la recomiendo tanto, ya que si estamos importando una gran cantidad de objetos el código puede tornarse difícil de leer, e inclusive, difícil de mantener. Te recomiendo que, si importarás en una sola línea, intente hacerlo con la menor cantidad de objetos posibles.

Ahora, en caso queramos definir el origen una sola vez, pero no queremos tener todo en una sola línea, podemos hacer uso de paréntesis ().

from entities import (Seller,
                Customer,
                Admin)

En lo personal esto no me termina de convencer, pero por lo menos ya es mucho más legible.

Otra forma de importar múltiples objetos de un mismo un mismo origen es utilizando asterisco (*)

from entities import *

De esta forma le indicamos Python que vamos a importar todos los objetos del módulo o paquete.

De todas las formas posibles de importar esta es la que menos me gusta, ya que perdemos un completo control sobre los objetos que realmente necesitemos, pudiendo así importar objetos que nunca vayamos a necesitar. 🤯

Es cierto, el código se reduce y es mucho más legible, sin embargo el ganar legibilidad implica sacrificar performance de la aplicación, ya que el importar objetos es algo costoso en términos computacionales, logrando así que la aplicación se vuelva lenta.

Si lo objetos que vamos a importar son de módulos o paquetes propios, importarlos utilizando asterisco (*) puede tener sentido, ya que nosotros sabremos exactamente que estamos importando, sin embargo si estamos importando de un módulo o paquete de un tercero te recomiendo seas precavido, e investigues exactamente que estás importando y si realmente te será de utilidad.

Renombrar imports 🕵️‍♀️

En caso el nombre del objeto que vamos a importar entra en conflicto con algún otro objeto del namespace lo que podemos hacer es renombrarlo. Para ello vamos a usar la palabra reservada as.

Ejemplo.

main.py

from entities import Admin as EntitieAdmin

class Admin:
    pass

admin1 = EntitieAdmin()

Para este ejemplo estoy renombrando la clase Admin por EntitieAdmin, esto con la finalidad de no entrar en conflicto con la clase del archivo main.py.

El rename podemos hacerlo para cualquier objeto que deseemos. Esto, te aseguro, te será de mucha útilida.

Import bajo contextos.

Algo que sin duda debemos tener muy presentes es el alcance, de cada import. Recordemos que en Python todo es un objeto, esto, por supuesto, incluye variables, funciones, clases, módulos, paquetes etc... Vaya todo lo que podemos importar. Y como objetos estos tienen un alcance, un contexto donde pueden ser utilizados. 😵‍💫

Ejemplos.

main.py

from entities import User


def create_user():
    user1 = User()
    return user1


if __name__ == '__main__':
    create_user()

Para este ejemplo, como el import se encuentra en el primer bloque del módulo main, los objetos importados pueden ser utilizados en todos lo sub-bloques (scopes). Por lo tanto podemos crear un objeto dentro de la función create_user sin ningún problema.

Hasta aquí, probablemente, nada nuevo.

Sin embargo, déjame decirte que en Python nosotros podemos hacer el import en el bloque que deseemos.

Veamos.

main.py

def create_user():
    from entities import User

    user1 = User()
    return user1


if __name__ == '__main__':
    create_user()

Ahora he modificado ligeramente el archivo. Ahora el import se realiza dentro de la función create_user , por lo tanto la clase User solo podrás ser utilizada dentro de esta función. Si se intenta usar dicha clase fuera del scope donde fue importada una excepción será lanzada. 🤖

def create_user():
    from entities import User

    user1 = User()
    return user1


if __name__ == '__main__':
    # Utilizamos la clase en un bloque donde no fue importada.
    user1 = User() 

Error.

Traceback (most recent call last):
  File "main.py", line 9, in <module>
    user1 = User()
NameError: name 'User' is not defined

Los import podemos realizarlos donde necesitemos, ya sea en módulos, funciones, métodos, condiciones, ciclos etc...

Se que esto en primera instancia puede sonar algo descabellado y que no tiene ninguna utilidad, pero puedo asegurarte que es todo lo contrario, especialmente en todos aquellos casos en los cuales solo es necesario importar un objeto bajo ciertas circunstancias. Pudiendo así mejorar el performance de la aplicación e inclusive evitar posibles errores. 🍻


¿El contenido te resulto de ayuda?

Para poder dejar tu opinión es necesario ser un usuario autenticado. Login

Más Tips y Ejercicios 🐍

Adquiere una subscripción PyWombat por tan solo $3 USD. al mes.

Conoce los beneficios de ser usuario premium:
Niveles desbloqueados: Ten accesos a todos los niveles de ejercicios. 🔓
Nuevo límite: Incrementa tu límite de ejercicios por semana. 🚀
Contenido único: Recibe semanalmente recursos exclusivos de Python (Videos, Artículos y Capitulos del libro PyWombat, comienza como desarrollador Python. 🐍