Contenido del capítulo

Empezamos ya con la importación de módulos en Python. Aprenderás a importar módulos de Python y a utilizarlos desde tus propios archivos .py.

En este capítulo se trata un poco el tema de los espacios de nombres. Es un tema que tiene un capítulo dedicado más adelante. Si no los entiendes bien, no te preocupes, lo entenderás al llegar ahí.

Duración estimada de todo el contenido:
Duración del vídeo:
Contiene 4 ejercicios Contiene 1 vídeo.
Tabla de contenidos
Logo

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

    Importaciones y uso de módulos en Python
    Importaciones y uso de 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)
    Resultado en la consola
    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())
    Resultado en la consola
    ['__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())
    Resultado en la consola
    ['__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))
    Resultado en la consola
    ['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)
    Error en la consola
    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.

    Para solucionarlo, deja de utilizar la importación completa de random en el código, o bien, importa todo del módulo, sin utilizar from.

    El término from significa en español “de”.

    Por ejemplo, si escribimos from random import randint, estamos indicándole al intérprete lo siguiente: “Del módulo random, importa randint”.

    Podemos ver lo que se está cargando en el espacio de nombres nuevamente con dir():

    from random import randint
    print(dir())
    Resultado en la consola
    ['__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())
    Resultado en la consola
    ['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())
    Resultado en la consola
    ['__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))
    Resultado en la consola
    ['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ón dir() entre los paréntesis.

    Si quieres ver la importación del propio espacio de nombres sobre el que ejecutas dir() (espacio de nombres actual), ejecuta la función sin argumentos, tal y como he hecho anteriormente.

    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 primera significa “importar todo”, la segunda, “importación de estrella” (debido a la forma de estrella que tiene el asterisco) y la tercera “importar todo” (all y everything son sinónimos en algunos casos).

    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 *
    Error en la consola
    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())
    Resultado en la consola
    ['__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))
    Error en la consola
    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.

    Por ejemplo, si importas random y le das el nombre rd de alias, no podrás nombrar al módulo (en esa misma hoja de Python) mediante el nombre random, ahora lo tendrás que llamar rd.

    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.

    Por ejemplo, la mayoría de desarrolladores utilizan el alias tk con tkinter, o el alias np para numpy.

    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
    Error en la consola
    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.

    Por eso es altamente recomendable colocar siempre primero las importaciones.

    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.

    Te enseñaré pronto de qué forma puedes instalar módulos externos a 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)
    Resultado en la consola
    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):

    A = π r2
    A = π * radio2
    A = PI * radio * radio

    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 constante PI, 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.

    Si no quieres utilizar math, tendrás que definir la constante por tu cuenta buscando su valor en internet.

    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))
    Resultado en la consola
    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))
    Resultado en la consola
    ['__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ódulo math no cumple con la convención de nombres de constante de Python (el nombre en mayúsculas).

    Este nombre, y muchos otros, se crearon antes que la convención de las constantes, y se ha mantenido igual desde entonces, por retrocompatibilidad.

    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í:

    Resultado en la consola
    12.566370614359172

    El motivo de que con la constante pi de math salga un resultado diferente, es por la precisión decimal.

    La constante PI que definí en la primera solución tiene un valor de 3.14159, mientras que la que tiene el módulo math, tiene un valor de 3.141592653589793.

    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))
    Resultado en la consola
    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