¿Cómo se usan los módulos en Python?

Hay módulos como random
que vienen integrados
en el lenguaje Python, pero que tendremos que importar
para utilizar.
El término random significa en español aleatorio.
Por otro lado, hay módulos como builtins
, que
están disponibles de forma predeterminada en el intérprete
de Python, es decir, no hay que importarlos para usarlos.
También, puedes instalar módulos externos que no vengan incluidos en la instalación, con PIP, o con herramientas similares.
Espacio publicitario
Importar un módulo integrado en Python
Empecemos por la importación de un módulo integrado de
Python. Por ejemplo, random
.
Para este propósito, utilizaremos la palabra reservada
import
, seguido de un espacio y el nombre del
módulo a importar:
import random
Con esto ya tenemos acceso en nuestra hoja de código, a
todo el código que lleva escrito el módulo
random
.
Este módulo está dedicado a hacer cosas aleatorias de diversos tipos.
El término import significa en español importar.
Llamar a un elemento de un módulo
Para usar algo del módulo, debemos nombrarlo y poner un punto con el elemento que queremos utilizar.
Por ejemplo, voy a hacer una llamada al método
randint()
, la cual, genera un número entero
aleatorio según un rango proporcionado entre los
paréntesis. Aquí puedes ver de qué forma funciona:
# Importa el módulo random
import random
# Genera un número aleatorio entre 1 y 1000
aleatorio = random.randint(1,1000)
print(aleatorio)
789
Al ejecutarlo me ha salido el valor 789
, pero
verás que el número que aparece es aleatorio y va
cambiando cada vez que ejecutas el programa.
El rango posible, según los valores que he puesto entre
los paréntesis, es de un número de tipo int
,
desde el 1
hasta el 1000
.
La sintaxis de la llamada a un elemento de un módulo cualquiera es muy simple:
modulo.nombre_elemento
Eso sí, para hacer esta llamada deberás tener siempre
escrito el import
que enlace con el módulo
correspondiente. Exceptuando los módulos disponibles en el
intérprete de Python, como puede ser
builtins
. Por ejemplo, la función
print()
está disponible en este módulo
llamado builtins
. Para esta función no
tenemos que hacer una importación, ni nombrar el acceso al
módulo; esto ya viene implícito.
Espacio publicitario
La función predefinida dir()
es capaz de
devolver en una lista los nombres definidos en el espacio
de nombres. Puedes verlos en la consola así:
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
Recuerda que anteriormente también utilizamos la función
dir()
para devolver los miembros de un objeto pasado como argumento. Hay diferentes posibilidades con esta función.
Esto te lo muestro, para que puedas ir viendo lo que se carga en el espacio de nombres, cuando importas módulos.
Lo que ves en la salida, es la carga de elementos implícita que tiene Python, sin importar ningún módulo de forma explícita.
Con la importación aparece esto en el espacio de nombres:
import random
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'random']
Puesto que ahora tenemos random
cargado en el
espacio de nombres, podemos acceder a su contenido.
Si quieres indagar en los nombres que tiene el propio módulo, puedes accederlos así:
import random
print(dir(random))
['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'SystemRandom', 'TWOPI', '_ONE', '_Sequence', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_accumulate', '_acos', '_bisect', '_ceil', '_cos', '_e', '_exp', '_fabs', '_floor', '_index', '_inst', '_isfinite', '_lgamma', '_log', '_log2', '_os', '_pi', '_random', '_repeat', '_sha512', '_sin', '_sqrt', '_test', '_test_generator', '_urandom', '_warn', 'betavariate', 'binomialvariate', 'choice', 'choices', 'expovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 'paretovariate', 'randbytes', 'randint', 'random', 'randrange', 'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']
Importar parte de un módulo
En Python puedes importar parte de un módulo con la
palabra clave from
. Esto te permite acceder
directamente a las funciones o atributos que necesitas,
sin tener que utilizar el nombre completo del módulo cada
vez.
Por ejemplo, si solo necesitamos utilizar
randint()
, de todo lo que tiene el módulo
random
, puedes hacer esto:
# Importa la función randint del módulo random
from random import randint
# Genera un número aleatorio entre 1 y 999, y lo imprime
aleatorio = randint(1,1000)
print(aleatorio)
Así, ya puedes utilizar randint()
sin tener
que repetir la palabra random
.
Espacio publicitario
Si quieres utilizar más cosas del módulo, tendrás que separar los elementos que quieras por comas. De esta forma:
from random import randint, randrange
Esto hace la importación de randint()
y de
randrange()
, en una misma línea.
En Python, de forma general, importar parte de un módulo frente a todo completo, no tiene un impacto significativo en cuanto a uso de memoria. Se trata principalmente de una cuestión de legibilidad, para mostrar de forma explícita que cosas de un módulo se están utilizando.
No solo eso, también sirve para utilizar otra sintaxis más reducida en el acceso de los elementos, y también para no “contaminar” el espacio de nombres.
Ten en cuenta, que si utilizas from
, después
no podrás llamar al elemento con el nombre del módulo. Si
lo haces, obtendrás un error como este:
from random import randint, randrange
aleatorio = random.randint(1,1000)
print(aleatorio)
NameError: name 'random' is not defined. Did you forget to import 'random'?
Error: El nombre 'random' no está definido. ¿Olvidó importar 'random'?
El error indica que quizás hemos olvidado importar el módulo, ya que para el intérprete, el módulo
random
no está importado, solo una parte de él.
El término from significa en español “de”.
Podemos ver lo que se está cargando en el espacio de
nombres nuevamente con dir()
:
from random import randint
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'randint']
Esta vez, no aparece random
, solo
randint
. Esto nos demuestra, que solo estamos
importando randint
y no todo el módulo, con
todos sus elementos.
Espacio publicitario
Importar todos los elementos de un módulo
Otra opción menos recomendable, es la de importar todos
los elementos de un módulo con el comodín *
.
Este elemento representa un “todo”.
Aquí tienes un ejemplo:
# Importa todo del módulo random
from random import *
Digo que esta opción es menos recomendable, ya que en códigos grandes, será difícil ver qué elementos pertenecen al código base de Python, y cuáles pertenecen a un módulo, quizás sin saber a cuál pertenecen específicamente, si se están realizando varias importaciones.
De la otra forma, se ve muy claro si algo pertenece a un módulo, y a qué módulo en concreto.
El uso de import *
, tiene una ventaja clara.
Importas todo de un módulo, y te despreocupas de estar
indicando su nombre al querer utilizar uno de sus
elementos.
Sin embargo, algunas de las desventajas son para tenerlas muy en cuenta:
- Mala legibilidad del código.
- Si no conocemos todo el módulo de arriba a abajo, no sabemos todo lo que se ha importado.
- Frecuentemente, complica y corrompe el espacio de nombres. Hablaré muy pronto de este tema, pero básicamente es que si hay dos elementos con el mismo nombre, en el mismo espacio de nombres, entraremos en conflicto.
- Depuración de errores más complicada.
Aquí tienes representados los elementos del espacio de nombres en ambos casos:
from random import *
print(dir())
['Random', 'SystemRandom', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'betavariate', 'binomialvariate', 'choice', 'choices', 'expovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 'paretovariate', 'randbytes', 'randint', 'random', 'randrange', 'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']
import random
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'random']
En la segunda opción tenemos un espacio de nombres limpio. En la primera, con una sola importación ya se está complicando todo.
Espacio publicitario
Anteriormente, he escrito una salida en la consola que
mostraba todos los elementos del espacio de nombres en el
propio módulo, al hacer una importación normal como “import random
”.
Si comparas este tipo de importación con la del asterisco, verás que con el asterisco realmente no se cargan todos los nombres. Esto ocurre, por qué hay ciertas excepciones:
-
Nombres que comienzan con un guion bajo
(
_
): Estos nombres se consideran privados o protegidos y no se importan. - Nombres que están en mayúsculas: Por convención, estos nombres se consideran constantes y no se importan de esta forma.
- Nombres que ya están presentes en el espacio de nombres: Si un nombre ya está definido en el espacio de nombres actual, no se importará. Prevalece el que está en el espacio de nombres actual.
En el siguiente ejemplo, he marcado todos los nombres que se omiten si hacemos una importación con el asterisco:
import random
print(dir(random))
['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'SystemRandom', 'TWOPI', '_ONE', '_Sequence', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_accumulate', '_acos', '_bisect', '_ceil', '_cos', '_e', '_exp', '_fabs', '_floor', '_index', '_inst', '_isfinite', '_lgamma', '_log', '_log2', '_os', '_pi', '_random', '_repeat', '_sha512', '_sin', '_sqrt', '_test', '_test_generator', '_urandom', '_warn', 'betavariate', 'binomialvariate', 'choice', 'choices', 'expovariate', 'gammavariate', 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 'paretovariate', 'randbytes', 'randint', 'random', 'randrange', 'sample', 'seed', 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 'weibullvariate']
En esta salida en la consola se está imprimiendo el espacio de nombres del módulo
random
, no el espacio en el que se está haciendo la importación. Fíjate lo que tiene la funcióndir()
entre los paréntesis.
Si nos detenemos a pensar por qué se descartan los elementos que empiezan por guion bajo, encontramos el sentido de este comportamiento.
Recuerda la interfaz pública y la no pública. Los elementos privados o protegidos, no deberán accederse desde una fuente externa.
Entonces, no tendría sentido que el propio intérprete de Python, nos importara esas cosas que no debemos acceder por convención.
La forma de importar con
import *
, muy a menudo se dice en inglés como import all, star import o import everything.
La importación con *
no se permite a nivel de
función, solo a nivel de módulo.
Espacio publicitario
Aquí tienes un ejemplo:
def aleatorio():
from random import *
SyntaxError: import * only allowed at module level
Error de sintaxis: importar * solo está permitido a nivel de módulo
En cambio, sí que podríamos hacer esto con otros tipos de importaciones:
def aleatorio(a, b):
import random
print(random.randint(a, b))
aleatorio(10, 100)
Sin embargo, no te recomiendo que hagas esto, ya que complicas el código y lo haces menos legible.
Si nos fijamos en el espacio de nombres a nivel de módulo, la importación solo es accesible desde dentro de la función:
print(dir())
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'aleatorio']
Si intentas acceder desde fuera, recibirás el siguiente error:
def aleatorio(a, b):
import random
print(random.randint(a, b))
print(random.randint(10, 100))
NameError: name 'random' is not defined. Did you forget to import 'random'?
Error: El nombre 'random' no está definido. ¿Olvidó importar 'random'?
Importaciones múltiples
Puedes importar tantos módulos y elementos como necesites. Las importaciones no están limitadas a una sola línea.
Por ejemplo, en el siguiente código, estoy importando el
módulo math
, y solo
randint()
del módulo random
:
import math
from random import randint
Importaciones con alias
Es posible añadir importaciones con alias mediante la
palabra reservada as
.
Al poner un alias, podremos llamar de forma simplificada a un módulo o parte de él.
Empecemos importando un módulo cualquiera.
import random
aleatorio = random.randint(10, 100)
print(aleatorio)
Cada vez que tengamos que utilizar algo del módulo,
tendremos que escribir su nombre completo. Para no tener
que importar con el asterisco (*
), que crea
diversos problemas como he mencionado, puedes utilizar un
alias.
Aquí tienes un ejemplo:
import random as rd
aleatorio = rd.randint(10, 100)
print(aleatorio)
Esto no le resta legibilidad al código, y nos hace escribir de forma más ágil.
Con tan poco código puede parecer una tontería, pero cuando estés accediendo miles de veces a elementos de los módulos, verás lo práctico que es.
Si utilizas un alias en un archivo .py, deberás acceder al módulo con ese mismo alias. Ya no podrás utilizar el nombre completo.
También es posible hacer importaciones parciales con
from
, y utilizar un alias para ello.
Espacio publicitario
Aquí tienes un ejemplo:
from random import randint as rdi
aleatorio = rdi(10, 100)
print(aleatorio)
En este caso, no necesitamos escribir el nombre de la función o método. Ya viene integrado en el alias.
Los nombres que se le dan a los alias, son libres, les puedes poner lo que quieras, siempre y cuando respeten las normas de nombres de Python. No obstante, hay muchos módulos que se suelen escribir por todo el mundo con el mismo alias, casi por convención.
Importaciones y el flujo de ejecución
Por norma general, las importaciones se suelen hacer sobre las primeras líneas de código, antes que cualquier otra cosa. Esto para que los elementos importados estén disponibles en el flujo de ejecución, desde el principio.
No es obligatorio, pero sí recomendado.
Por ejemplo, si haces esto, recibes un error:
print(math.pi)
import math
NameError: name 'math' is not defined. Did you forget to import 'math'?
Error de nombre: El nombre 'math' no está definido. ¿Olvidó importar 'math'?
El error ocurre porque estoy intentando utilizar una variable del módulo
math
, que no se encuentra todavía importado en el flujo de ejecución. Esta importación, la estoy llevando a cabo después.
Resumen del capítulo
Te hago un resumen breve del capítulo, para asegurarme que no te dejas ningún concepto importante.
Espacio publicitario
Si quieres acceder a todo el contenido de un módulo, sin afectar al espacio de nombres y hacerlo de una forma más legible, utiliza esta forma de importación:
import nombre_módulo
Si quieres solo importar ciertos elementos de un módulo de una forma también legible, utiliza esta sintaxis:
from nombre_módulo import elemento1, elemento2,...
Si quieres importar todo de un módulo sin tener que especificar cada vez el nombre del módulo (menos recomendable), utiliza esta sintaxis:
from nombre_módulo import *
Si quieres darle otro nombre a una importación, utiliza un alias:
import numpy as np
Para utilizar
numpy
, necesitas instalarlo, puesto que no viene con la instalación de Python.
Espacio publicitario
Espacio publicitario
Ejercicios de Python para resolver
42. Accede a la función sqrt()
del módulo
math
, para calcular la raíz cuadrada de 81.
La importación la puedes haber hecho de diferentes
formas. Lo importante es que te funcione la función
sqrt()
. Si es así, lo has hecho perfecto.
# Importamos el módulo
import math
# Calculamos la raíz cuadrada
raiz = math.sqrt(81)
# Imprimimos el resultado
print(raiz)
9.0
43. Importa todos los elementos del módulo
math
.
Para importarlo todo, lo hacemos con el asterisco
(*
):
from math import *
44. Crea una función que sea capaz de calcular el área de un círculo.
La fórmula es la siguiente (escoge la forma que te sea más simple de entender):
Estas tres formas representan lo mismo, simplemente, la primera es más técnica y la última más simplificada.
En la función, deberás tener un parámetro, el de radio. Es decir, al llamar a la función, se le pasa el valor de radio del círculo, y la función deberá devolver el cálculo del área, gracias a la fórmula proporcionada.
El módulo
math
tiene su propia constantePI
, si la utilizas, te evitas tener que buscar el valor y crear por tu cuenta la constante.Utiliza la función predefinida
dir()
para ver el espacio de nombres del módulo, y encontrar así el nombre de la constante.
Para crear la función, tenías que aplicar correctamente la fórmula proporcionada.
Primero, debías crear tu propia constante
PI
, o bien utilizar la del módulo
math
.
Si no has utilizado el módulo math
, la
solución será más o menos como esta:
def calcula_area(radio):
# Definimos una aproximación de PI
PI = 3.14159
# Calculamos y devolvemos el área
area = PI * radio ** 2
return area
# Ejemplo de uso
print(calcula_area(2))
12.56636
En cambio, si has utilizado el módulo
math
, habrás tenido que investigar
primero de qué forma se llama la constante
pi
en el módulo. Esto se consigue muy
fácil con la función dir()
:
import math
print(dir(math))
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'cbrt', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'exp2', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'lcm', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'nextafter', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'sumprod', 'tan', 'tanh', 'tau', 'trunc', 'ulp']
La constante
pi
del módulomath
no cumple con la convención de nombres de constante de Python (el nombre en mayúsculas).
Una posible solución utilizando pi
del
módulo math
es esta:
# Importamos el módulo
import math
# Función para calcular áreas de círculos
def calcula_area(radio):
return math.pi * radio ** 2
print(calcula_area(2))
Y esta sería otra:
# Importamos el módulo
import math
# Función para calcular áreas de círculos
def calcula_area(radio):
return math.pi * radio * radio
print(calcula_area(2))
Independientemente de como lo hayas hecho, te debería calcular correctamente.
Por ejemplo, en ambas soluciones, el cálculo con un
2
de radio, sale así:
12.566370614359172
El motivo de que con la constante
pi
demath
salga un resultado diferente, es por la precisión decimal.
45. En los resultados, te saldrán muchos decimales. Limita estos a un máximo de 2, en el resultado que ofrece la llamada a la función.
Aquí tenías que redondear el resultado con
round()
, a dos decimales:
# Importamos el módulo
import math
# Función para calcular áreas de círculos
def calcula_area(radio):
return round(math.pi * radio * radio, 2)
print(calcula_area(2))
12.57
Por supuesto, si deseas hacer el código más legible, puedes guardar la operación en una variable, y luego devolver el resultado:
# Importamos el módulo
import math
# Función para calcular áreas de círculos
def calcula_area(radio):
area = round(math.pi * radio * radio, 2)
return area
print(calcula_area(2))
Espacio publicitario