Un módulo es simplemente un archivo de código Python, e importarlo te permite reutilizar lo que define. Al examen PCAP le importa menos que sepas importar y más qué nombres exactamente quedan disponibles después. Cada módulo y script tiene su propio namespace (espacio de nombres): una tabla que asocia nombres con objetos. Cada forma de import agrega entradas distintas al namespace que importa, y muchas preguntas del examen en realidad preguntan si un nombre concreto existe en un momento concreto.
La forma simple import math agrega exactamente un nombre, math, que se refiere al objeto módulo. Todo lo que hay dentro debe calificarse: math.pi, math.sqrt(2). Escribir sqrt(2) solo lanza NameError porque sqrt nunca se colocó en tu namespace. La forma con alias import math as m agrega solo m; el nombre math no está definido, así que math.pi falla mientras que m.pi funciona. Los alias son prácticos para nombres de módulo largos y son comunes con módulos como import numpy as np en el ecosistema más amplio.
La forma from math import sqrt, pi funciona al revés. Agrega sqrt y pi directamente, así que llamas sqrt(2) sin prefijo, pero el nombre math no está definido. También puedes poner alias a nombres individuales: from math import sqrt as root. Como los nombres caen directamente en tu namespace, pueden chocar con los tuyos: si más adelante escribes pi = 3, reemplazaste el valor importado, y si defines tu propia sqrt y luego importas una, gana el import porque ocurrió después. La última asignación (binding) siempre gana.
La forma con asterisco from math import * copia todos los nombres públicos del módulo a tu namespace. Si el módulo define una lista llamada __all__, solo se importan los nombres de esa lista; si no, se importa todo nombre que no empiece con guion bajo. Los star imports se desaconsejan en código real porque ocultan de dónde vino un nombre y pueden sobrescribir tus propios nombres sin avisar, pero el examen espera que conozcas las reglas del guion bajo y de __all__.
import math as m
print(m.floor(2.7)) # 2
# print(math.pi) # NameError: math no está definido
from math import pi
print(pi) # 3.141592653589793
pi = 3
print(pi) # 3 - tu asignación lo reemplazó
Dos datos más completan el tema. El código de un módulo se ejecuta solo en su primer import dentro de un proceso; los imports posteriores reutilizan el objeto módulo ya cargado, guardado en sys.modules. Y toda forma de import ejecuta el archivo completo del módulo la primera vez, incluso from mod import one_name, porque Python tiene que ejecutar el archivo para crear el nombre.
Términos clave
- Namespace (espacio de nombres)
- Una asociación de nombres con objetos; cada módulo, llamada a función y clase tiene el suyo.
- Alias
- Un nombre alternativo dado con as, por ejemplo import math as m, que enlaza solo m.
- __all__
- Una lista de cadenas en un módulo que indica qué debe importar from module import *.
- Qualified name (nombre calificado)
- Un nombre escrito con el prefijo de su módulo, como math.sqrt.
Un script de datos empieza con from statistics import mean y más adelante define su propia función llamada mean para manejar valores faltantes. Como el def viene después del import, todas las llamadas posteriores usan la versión local, y el import original queda ocultado (shadowed) sin ninguna advertencia.
Comprueba lo que sabes
Después de import random as r, ¿funciona print(random.random())?
No. Solo está definido el alias r, así que random lanza NameError; debes escribir r.random().
¿Qué nombres omite from mod import * cuando mod no tiene __all__?
Los nombres que empiezan con guion bajo, como _helper; todos los demás nombres de nivel superior se importan.
Si un módulo se importa dos veces en el mismo programa, ¿cuántas veces se ejecuta su código de nivel superior?
Una vez. El segundo import encuentra el módulo ya en sys.modules y lo reutiliza.