A medida que se desarrolla una aplicación y crece su complejidad, es conveniente dividirla en diversos archivos de código. Por otro lado, cuando uno escribe una cierta función útil para diferentes programa, uno quisiera poder usarla sin necesidad de copiar el código cada vez. Para esto, exsite una forma de poner funciones en archivos y usarlas desde un programa o desde el intérprete. Este tipo de archivo recibe el nombre de módulo.
Un módulo es un archivo que contiene instrucciones y definiciones. El nombre del archivo es el nombre del módulo con el sufijo .py. Veamos un ejemplo de un módulo definido en el archivo fibo.py. (nota: las instrucciones %load no son parte del código, se usan para desplegarlo)
%load ./fibo.py
# Modulo de números de Fibonacci
def fib(n): # escribe la serie de Fibonacci hasta el número n
a, b = 0, 1
while b < n:
print(b, end=' ')
a, b = b, a+b
print()
def fib2(n): # devuelve la serie de Fibonacci hasta el número n
resultado = []
a, b = 0, 1
while b < n:
resultado.append(b)
a, b = b, a+b
return resultado
Para hacer disponible el módulo se debe ejecutar la instrucción import en el intérprete o en un programa. Es recomendado poner las instrucciones de importación de módulos al inicio del código. Cabe notar que un módulo puede a su vez importar otros módulos.
import fibo
fibo.fib(1000)
fibo.fib2(100)
También se pueden importar funciones específicas del módulo.
from fibo import fib, fib2
fib(500)
El nombre del módulo está disponible a través de la variable global __name__
import fibo
fibo.__name__
Si el módulo se invoca como si fuera un programa, de la siguiente forma:
!python3 fibo.py
se ejecuta el código dentro del módulo (tal como al importarlo), pero con el __name__ como __main__.
Esto quiere decir que si se agrega el siguiente código al final del módulo:
%load fibo_exec.py
# Modulo de números de Fibonacci
def fib(n): # escribe la serie de Fibonacci hasta el número n
a, b = 0, 1
while b < n:
print(b, end=' ')
a, b = b, a+b
print()
def fib2(n): # devuelve la serie de Fibonacci hasta el número n
resultado = []
a, b = 0, 1
while b < n:
resultado.append(b)
a, b = b, a+b
return resultado
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
se logra que el módulo sea además un programa ejecutable, porque la última parte del código solo se ejecuta si el módulo es ejecutado como programa principal. Esto se usa en ocasiones para dar una interfaz de usuario conveniente para el módulo o para ejecutar funciones de comprobación. Veamos a continuación como se comporta el nuevo módulo al ser importado como módulo y al ser ejecutado como programa principal.
import fibo_exec
fibo_exec.fib(50)
!python3 fibo_exec.py 50
Cuando se importa un cierto módulo de nombre ritmo, el intérprete busca en primer lugar un módulo incoroporado (built-in) del lenguaje. Si no lo encuentra busca un archivo ritmo.py en una lista de directorios definidos en la variable sys.path. La variable se inicializa desde las siguientes ubicaciones:
!echo $PYTHONPATH
Python tiene una biblioteca de módulos estándar que está descripta en la Python Library Reference. Por ejemplo, el módulo sys está incorporado en todo intérprete de python. Un módulo puede definir variables, como sys.path que establece la lista de directorios dónde el intérprete busca un módulo al momento de importarlo.
import sys
sys.path
Es interesante notar que la variable sys.path se puede modificar en tiempo de ejecución con operaciones de listas.
Permite consultar los nombres (funciones, variables, módulos, etc) que define un módulo.
import fibo
dir(fibo)
import sys
dir(sys)
Los paquetes son una forma de organizar la nomeclatura de módulos (el espacio de nombres). Por ejemplo, el módulo de nombre A.B designa un sub-módulo llamado B dentro del paquete llamado A. Del mismo modo que el uso de módulos ayuda a que el programador no tenga que preocuparse por los nombres usados en otros módulos, el uso de módulos con punto permite que en paquetes de múltiples módulos como Matplotlib o NumPy no sea problemático el nombre de los módulos de otro paquete.
A modo de ejemplo consideremos la siguiente situación. Supóngase que se quiere diseñar una colección de módulos (un paquete) para el manejo y procesamiento de archivos de audio. En primer término, hay muchos tipos de archivo de audio (e.g. .wav, .aiff, .au, etc) por lo que se debería mantener una colección de módulos destinados a abrir, escribir y convertir entre diferentes formatos. En segundo lugar, hay muchos tipos de procesamiento que uno puede realizar sobre un archivo de audio, como ecualización, echo, filtrado, etc. En particular uno podría discriminar entre filtros y otro tipo de procesamiento más complejo (e.g. phase-vocoder, modelado sinusoidal, síntesis granular, etc.) y a su vez se podría separlos de efectos de audio clásicos (e.g. reverb, delay, phasor, flanger, etc.). A continuación hay una posible estructura de un paquete de este tipo.
%load sound.txt
sound/ Top-level package
__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Subpackage for sound effects
__init__.py
reverb.py
delay.py
echo.py
phasor.py
flanger.py
...
filters/ Subpackage for filters
__init__.py
equalizer.py
lowpass.py
highpass.py
bandpass.py
notch.py
...
processing/ Subpackage for sound processing and synthesis techniques
__init__.py
phasevocoder.py
sinusoidalmodeling.py
granularsynthesis.py
amplitudemodulation.py
frequencymodultaion.py
...
Cuando se importa el paquete Python busca a través de los subdirectorios definidos en sys.path el directorio del paquete. Los archivos __init__.py son necesarios para hacer que Python trate a los directorios como conteniendo paquetes. En este ejemplo sencillo los archivos __init__.py están vacíos, pero también pueden ser usados para ejectuar código de inicialización para el paquete.
Los módulos del paquete se pueden importar de forma independiente como en el siguiente ejemplo. Al hacer este tipo de importación hay que usar el nombre completo para referirse una función del módulo.
import sound.effects.echo
# se debe usar el nombre completo de la función
sound.effects.echo.echofilter(delay=0.7, atten=4)
Una forma alternativa de importar el sub-módulo es la siguiente. En este caso no es necesario usar el prefijo del paquete en el nombre de función.
from sound.effects import echo
# en este caso no hay que usar el prefijo del paquete en el nombre de la función
echo.echofilter(delay=0.3, atten=10)
Una forma de importar solo la función deseada es la siguiente. Esto hace que, además de cargarse el módulo, el nombre de la función esté disponible directamente.
from sound.effects.echo import echofilter
# el nombre de la función está accesible directamente
echofilter(delay=0.9, atten=12)
Cabe señalar que cuando se usa la instrucción from package import item, el item puede ser un sub-módulo (o un sub-paquete) del paquete, o cualquier otro nombre definido en el paquete, como una función o variable. Contrariamente, cuando se usa la sintaxis import item.subitem.subsubitem, cada item excepto el último debe ser un paquete; y el último item puede ser un módulo o un paquete, pero no puede ser una función o una variable definida en el item previo.
Si bien es una práctica no recomendada es posible importar el paquete de la siguiente forma.
from sound.effects import *
Para que esto funcione correctamente el programador del paquete debe proporcionar un índice explícito. La convención es la siguiente: si el código __init__.py define una lista llamada __all__ se considera que es la lista de nombres de los módulos a importar. Si por el contrario la definición no existe, la instrucción anterior no importa los submódulos del paquete. Es una responsabilidad del programador del paquete mantener la lista actualizada.
A continuación el contenido del archivo __init__.py para el módulo sound.effects.
%load sound/effects/__init__.py
__all__ = ["reverb", "delay", "echo", "phasor", "flanger"]
Y con la variable __all__ definida de esa forma se puede ver a continuación los módulos que son importados.
from sound.effects import *
dir(sound.effects)
Escriba un módulo cursopython.py con todas las funciones de los ejercicios anteriores. Vuelva a escribir los ejercicios importando el módulo. El nombre de archivo de cada ejercicio debe ser ejercicio_ seguido del número de ejercicio, por ejemplo ejercicio_7.1.py