9.2. Paquetes

Analizar un documento XML es algo muy sencillo: una línea de código. Sin embargo, antes de llegar a esa línea de código hará falta dar un pequeño rodeo para hablar sobre los paquetes.

Ejemplo 9.5. Carga de un documento XML (vistazo rápido)

>>> from xml.dom import minidom 1
>>> xmldoc = minidom.parse('~/diveintopython/common/py/kgp/binary.xml')
1 Esta sintaxis no la ha visto antes. Se parece al from módulo import que ya conoce y adora, pero el "." lo hace parecer algo más que un simple import. De hecho, xml es lo que conocemos como un paquete, dom es un paquete anidado en xmly minidom es un módulo dentro de xml.dom.

Suena complicado pero en realidad no lo es. Ver la implementación puede que ayude. Los paquetes son poco más que directorios de módulos; los paquetes anidados son subdirectorios. Los módulos dentro de un paquete (o paquete anidado) siguen siendo sólo ficheros .py, como siempre, excepto que están en un subdirectorio en lugar del directorio lib/ principal de la instalación de Python.

Ejemplo 9.6. Estructura de ficheros de un paquete

Python21/           instalación raíz de Python (directorio del ejecutable)
|
+--lib/             directorio de bibliotecas (lugar de los módulos estándar)
   |
   +-- xml/         paquete xml (un simple directorio con otras cosas dentro)
       |
       +--sax/      paquete xml.sax (de nuevo, sólo un directorio)
       |
       +--dom/      paquete xml.dom (contiene minidom.py)
       |
       +--parsers/  paquet xml.parsers (de uso interno)

Así que cuando decimos from xml.dom import minidom, Python interpreta eso como “busca el directorio dom en xml, y luego busca el módulo minidom ahí, e impórtalo como minidom”. Pero Python es incluso más inteligente; no sólo puede importar módulos enteros contenidos en un paquete; puede importar deforma selectiva clases o funciones específicas de un módulo contenido en un paquete. También puede importar el paquete en sí como un módulo. La sintaxis es la misma; Python averigua lo que usted quiere basándose en la estructura de ficheros del paquete, y hace lo correcto de forma automática.

Ejemplo 9.7. Los paquetes también son módulos

>>> from xml.dom import minidom         1
>>> minidom
<module 'xml.dom.minidom' from 'C:\Python21\lib\xml\dom\minidom.pyc'>
>>> minidom.Element
<class xml.dom.minidom.Element at 01095744>
>>> from xml.dom.minidom import Element 2
>>> Element
<class xml.dom.minidom.Element at 01095744>
>>> minidom.Element
<class xml.dom.minidom.Element at 01095744>
>>> from xml import dom                 3
>>> dom
<module 'xml.dom' from 'C:\Python21\lib\xml\dom\__init__.pyc'>
>>> import xml                          4
>>> xml
<module 'xml' from 'C:\Python21\lib\xml\__init__.pyc'>
1 Aquí estamos importando un módulo (minidom) de un paquete anidado (xml.dom). El resultado es que se importa minidom en el espacio de nombres, y para referirnos a las clases dentro del módulo minidom habrá que prefijarlos con el nombre del módulo.
2 Aquí estamos importando una clase (Element) de un módulo (minidom) que pertenece a un paquete anidado (xml.dom). El resultado es que se importa Element directamente en el espacio de nombres. Observe que esto no interfiere con la importación anterior; ahora nos podemos referir a la clase Element de dos maneras (pero siempre es la misma clase).
3 Aquí estamos importando el paquete dom (un paquete anidado en xml) como si fuera un módulo. A un paquete de cualquier nivel se le puede tratar como un módulo, como verá enseguida. Incluso puede tener sus propios atributos y métodos, igual que los módulos que ya hemos visto.
4 Aquí estamos importando el paquete raíz xml como módulo.

Entonces, ¿cómo puede ser que importemos y tratemos un paquete (que es sólo un directorio en el disco) como un módulo (que siempre es un fichero)? La palabra es el fichero mágico __init__.py. Como ve, los paquetes no son simples directorios; son directorios con un fichero especial dentro, __init__.py. Este fichero define los atributos y métodos del paquete. Por ejemplo, xml.dom contiene una clase Node que está definida en xml/dom/__init__.py. Cuando importa un paquete como módulo (como dom de xml), en realidad está importando su fichero __init__.py.

nota
Un paquete es un directorio que contiene el fichero especial __init__.py. El fichero __init__.py define los atributos y métodos del paquete. No tiene por qué definir nada; puede ser un fichero vacío, pero ha de existir. Pero si no existe __init__.py el directorio es sólo eso, un directorio, no un paquete, y no puede ser importado o contener módulos u otros paquetes.

Así que, ¿por qué usar paquetes? Bueno, proporcionan una manera lógica de agrupar módulos relacionados. En lugar de tener un paquete xml que contenga los paquetes sax y dom, los autores podrían haber escogido poner toda la funcionalidad de sax en xmlsax.py y toda la funcionalidad de dom en xmldom.py, o incluso ponerlo todo en un único módulo. Pero eso hubiera sido horrible (mientras escribo esto, el paquete XML contiene más de 3000 líneas de código) y difícil de mantener (los ficheros fuente separados implican que varias personas pueden trabajar simultáneamente en aspectos diferentes).

Si alguna vez se encuentra escribiendo un subsistema grande en Python (o más bien, cuando se dé cuenta de que su pequeño subsistema ha crecido mucho), invierta algo de tiempo en diseñar una buena arquitectura de paquete. Ésta es una de las muchas cosas en que Python destaca, así que aprovéchelo.