2.12: Interfaces de Programación de Aplicaciones, Módulos, Paquetes, Azúcar Sintáctico
- Page ID
- 55099
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)Sabemos que los objetos como listas tienen métodos como .sort ()
y .append ()
. ¿Tienen otros? Lo hacen, y una forma de encontrarlos es ejecutar Python en el modo interactivo en la línea de comandos. [1]
Para ello, ejecutamos el intérprete python
sin especificar un archivo a interpretar. El resultado es un prompt de Python, >>>
, donde podemos escribir líneas individuales de código Python y ver los resultados.

El modo interactivo, además de proporcionar una interfaz para ejecutar pruebas rápidas de la funcionalidad de Python, ¡incluye un sistema de ayuda! Simplemente ejecute la función help ()
en un nombre de clase (como help (list))
o en una instancia de la clase.

Este comando abre un visor interactivo en el que podemos desplazarnos para ver todos los métodos que proporciona un objeto de ese tipo. Aquí hay una muestra de la página de ayuda:

Navegar por esta documentación revela que las listas de Python tienen muchos métodos, incluyendo .append ()
, .count ()
y otros. A veces la documentación no es tan clara como cabría esperar. En estos casos, puede requerirse alguna experimentación. Entre la descripción anterior y algunas pruebas de código, por ejemplo, ¿puedes determinar qué hace el método .count ()
de una lista y cómo usarlo de manera efectiva?
El conjunto de métodos y variables de instancia que pertenecen a un objeto o clase se conoce como su API, o Interfaz de Programación de Aplicaciones. La API es el conjunto de funciones, métodos o variables proporcionadas por una colección encapsulada de código. Las APIs son una parte importante de la programación porque representan la “interfaz de usuario” para los constructos de programación que otros han escrito.
En ocasiones puede ser útil determinar qué clase o “tipo” a la que se refiere una variable, especialmente para buscar en la API objetos de ese tipo. La función type ()
es útil cuando se combina con una llamada print ()
: imprime el tipo (la clase) de datos referenciados por una variable. El resultado puede entonces ser investigado con help ()
.

Módulos
Considere este trozo de código del ejemplo del capítulo 23, “Objetos y clases”, que hace uso de las definiciones de las clases Cromosoma
y SNP
:

Este segmento de código toma un nombre de archivo y produce un diccionario con contenidos bien organizados del archivo. Debido a que esta funcionalidad está definida de manera sucinta, tiene sentido convertirla en una función que tome el nombre del archivo como parámetro y devuelva el diccionario. El código es casi exactamente el mismo, excepto por estar envuelto en una definición de función.

Posteriormente, podemos llamar a esta función para hacer todo el trabajo de analizar un nombre de archivo dado:

Ahora bien, ¿y si esta función y las dos definiciones de clase fueran cosas que queríamos usar en otros proyectos? Es posible que deseemos hacer un módulo con ellos, un archivo que contenga código Python (generalmente relacionado con un solo tema, como un conjunto de funciones o definiciones de clase relacionadas con un tipo particular de datos o procesamiento).
Ya hemos visto varios módulos, incluyendo io
, re
y sys
. Para usar un módulo, solo necesitamos ejecutar import modulename
. Resulta que los módulos son simplemente archivos de código Python que terminan en ¡.py
! Así, cuando usamos import modulename
, el intérprete busca un modulename.py
que contenga las distintas definiciones de función y clase. Los archivos de módulo pueden existir en una ubicación de todo el sistema, o estar presentes en el directorio de trabajo del programa importándolos. [2]
Importar el módulo de esta manera crea un espacio de nombres para el módulo. Este espacio de nombres categoriza todas las funciones y clases contenidas dentro de ese módulo para que diferentes módulos puedan tener funciones, clases y variables con el mismo nombre sin conflicto.

Si tenemos los dos archivos anteriores en el mismo directorio que nuestro programa, el código fuente como el siguiente imprimiría 4
, 64
, “TAC”
y 2.71828
.

Desafortunadamente, Python usa el.
operador de múltiples maneras similares, como indicar un método que pertenece a un objeto (como en a_list.append (“ID3"))
y para indicar una función, variable o definición de clase perteneciente a un espacio de nombres (como arriba).
Así, como un “contenedor” para los nombres, el espacio de nombres permite a diferentes módulos nombrar funciones, clases y variables de la misma manera sin entrar en conflicto. Así como una clase puede ser documentada por su API, también lo pueden hacer los módulos: la API de un módulo describe las funciones, clases y variables que define. También se puede acceder a ellos a través del menú de ayuda interactiva (y en línea). Intente ejecutar import re
seguido de help (re)
para ver todas las funciones proporcionadas por el módulo re
.

Hay una gran cantidad de módulos (y paquetes) disponibles para descargar en línea, y Python también viene con una amplia variedad de módulos útiles por defecto. Aparte de re
, sys
e io
, discutidos anteriormente, algunos de los módulos potencialmente más interesantes incluyen:
string
: operaciones de cadena comunes (cambio de mayúsculas y minúsculas, formateo, etc.)hora
,fechahora
ycalendario
: operaciones en fechas y horasrandom
: generar y trabajar con números aleatoriosargparse: análisis
fácil de usar de argumentos complejos en la línea de comandosTkinter
: creación de interfaces gráficas de usuario (con botones, barras de desplazamiento, etc.)unittest
: automatizar la creación y ejecución de pruebas unitariasturtle
: una interfaz simple para mostrar gráficos basados en líneas [3]
Los tutoriales para estos módulos específicos se pueden encontrar en línea, así como listas de muchos otros paquetes y módulos que vienen instalados con Python o que están disponibles para su descarga.
Creación de un módulo
Vamos a crear un módulo llamado MyVCFModule.py
para contener las dos definiciones de clase y la función de análisis del último ejemplo. Mientras lo hacemos, también convertiremos los comentarios que habíamos creado para nuestras clases, métodos y funciones en “docstrings”. Las cadenas de documentos son cadenas de comillas triples que proporcionan una funcionalidad similar a los comentarios, pero se pueden analizar posteriormente para producir la API del módulo. Pueden abarcar varias líneas, pero deben ocurrir inmediatamente dentro de la definición de módulo, clase o función correspondiente. Aquí dejamos fuera el contenido de los métodos y funciones (que son los mismos que en el capítulo anterior) para ahorrar espacio:

El programa que hace uso de este módulo, que existe en un archivo separado en el mismo directorio (quizás llamado snps_ex2.py
), es corto y dulce. [4]

Si otros proyectos necesitan analizar archivos VCF, este módulo puede ser de nuevo de utilidad. Por cierto, debido a que documentamos nuestro módulo con docstrings, su API es fácilmente accesible a través de la interfaz de ayuda interactiva.

Parte de la página de ayuda de la API:

Paquetes
Como si Python no tuviera ya suficientes “cajas” para la encapsulación —funciones, objetos, módulos, etc.— también hay paquetes. En Python, un “paquete” es un directorio que contiene algunos archivos de módulo. [5]
Un directorio de paquetes también debe contener un archivo especial llamado __init__.py
, que le permite a Python saber que el directorio debe tratarse como un paquete desde el que se pueden importar módulos. (Se podría poner código en este archivo que se ejecutaría cuando se ejecute la sentencia import
, pero no exploraremos esta característica aquí).

Como ejemplo, supongamos que junto con nuestro MyVCFModule.py
, también habíamos creado un módulo para analizar archivos de ontología génica llamado GOParseModule.py
. Podríamos juntarlos en un paquete (directorio) llamado MyBioParsers
.
Para usar un módulo contenido en un paquete, la sintaxis es de packagename import modulename
. [6] Nuestro programa Python podría vivir en el mismo directorio en el que se encontró el directorio MyBioParsers
, y podría comenzar así:

Posteriormente, el propio módulo se puede utilizar igual que antes.

Analizar un archivo FASTA
Hasta este punto, nos hemos saltado algo que es ampliamente considerado un “básico” en biología computacional: leer un archivo FASTA. En su mayor parte, los ejemplos anteriores que necesitan datos de secuencia leen esos datos de archivos simples con formato de fila/columna.

La mayoría de los datos de secuencia, sin embargo, aparecen en el llamado formato FASTA, donde cada secuencia tiene una línea de cabecera que comienza con >
y un ID, y la secuencia se divide en cualquier número de líneas siguientes antes de que aparezca la siguiente línea de cabecera.

Analizar datos en formato FASTA no es demasiado difícil, pero requiere un paso de preprocesamiento adicional (generalmente implica un bucle y una variable que realiza un seguimiento del ID “actual”; siempre que las líneas posteriores no coincidan con el carácter >
, deben ser líneas de secuencia y pueden agregarse a una lista de cadenas para luego unirse para esa identificación). Si bien reinventar la rueda de análisis FASTA es una buena práctica para los codificadores novatos, el paquete BioPython proporciona funciones para analizar estos y muchos otros formatos. [7]
BioPython es un paquete grande con muchos módulos y APIs, sobre los que puedes leer más en http://biopython.org. Para la aplicación específica de análisis de un archivo FASTA, un buen lugar para comenzar es una simple búsqueda web de “biopython fasta”, que nos lleva a una página wiki que describe el módulo SeqIO, completa con un ejemplo.

Esta captura de pantalla muestra algunas diferencias con la forma en que hemos estado escribiendo código, por ejemplo, el uso de open ()
en contraposición a io.open ()
, y la llamada a print record.id
en lugar de nuestra habitual print (record.id)
(la primera de las cuales funcionará solo en versiones anteriores de Python, mientras que esta última funcionará en todas las versiones razonablemente recientes de Python). Sin embargo, dado nuestro conocimiento de paquetes, módulos y objetos, podemos deducir lo siguiente de este sencillo ejemplo de código:
- El módulo
SeqIO
se puede importar desde el paqueteBio
. - Se abre un manejador de archivo.
- Se llama a la función
seqio.parse ()
, y toma dos parámetros: el identificador del archivo desde el que leer los datos, y una cadena de especificación que describe el formato del archivo. - Esta función devuelve algo iterable (similar a una lista, o un identificador de archivo normal), por lo que se puede enrollar con un bucle for.
- El bucle accede a una serie de objetos de algún tipo, y cada uno se asocia con el
registro
de nombre de variable. - Se imprime el
registro.id
, que es (aparentemente) una variable de instancia que pertenece al objetorecord
. - El mango del archivo está cerrado (¡siempre buena práctica!).
Si hacemos un poco más de lectura en la página wiki de SeqIO
, encontraríamos que los objetos de registro
realmente tienen la clase SeqRecord
, y también tienen una variable de instancia seq
que proporciona acceso a la secuencia completa.
Al juntar todo esto, aquí hay un programa corto que convierte un archivo FASTA al formato de fila/columna con el que hemos estado tratando.

Azúcar Sintáctico
En Python, casi todo es un objeto, ¡incluso enteros simples! Al mirar la API de un entero, por ejemplo, descubrimos que proporcionan un método llamado .bit_length ()
.

Aquí hay una parte de la vista de API:

Podemos probarlo así, para descubrir que el entero 7
se puede representar con tres bits binarios (como 111
):

Si intentaras ver la API para un entero como lo hemos hecho, verías una serie de métodos de aspecto impar, como. __add__ ()
y. __abs__ ()
:

Esto parece indicar que podemos obtener el valor absoluto de un entero mediante el uso de una llamada a un método o mediante el uso de la sintaxis de llamada a la función estándar. Del mismo modo, aparentemente podemos agregar un entero a otro mediante el uso de la llamada al método o el operador estándar +
. Estos son realmente ciertos, y podemos usar las funciones y operadores estándar o sus versiones de método:

Las operaciones como la adición parecen operaciones básicas y fundamentales, y Python realiza tales operaciones a través de llamadas a métodos en objetos. [8] Una declaración como a = b + c
se convierte en a = b.__add__ (c)
detrás de escena. Aunque podemos ejecutar tales operaciones orientadas a métodos nosotros mismos, la incómoda sintaxis de doble subrayado es cómo los diseñadores nos hacen saber que esos métodos son para uso interno, y debemos seguir con la sintaxis estándar. Esta conversión automática de sintaxis se conoce como azúcar sintáctica, y está presente en muchos lenguajes de programación para que puedan ser internamente consistentes pero más agradables (“más dulces”) para que los humanos trabajen con ellos.
Ejercicios
- Cree un módulo llamado
mymodule.py
que incluya al menos una definición de clase y definición de función, así como documente el módulo, clase, métodos y funciones con docstrings. Crear unmyprogram.py
que importe este módulo y haga uso de la funcionalidad en él. También intente ver la API para su módulo en el intérprete interactivo de Python conimport mymodule
yhelp (mymodule)
. - Navegue a través de la API en línea en http://docs.python.org/2/library para obtener la “Biblioteca estándar de Python” y especialmente cualquier cosa relacionada con cadenas. Además, revise la documentación de la API para los módulos
io
,sys
,re
,math
yrandom
. - Para este ejercicio, vamos a instalar un módulo desde la web y hacer uso de él para analizar un archivo VCF
trio.sample.vcf
. El módulo de lectura VCF que vamos a instalar se encuentra en GitHub (un lugar cada vez más popular para alojar software): https://github.com/jamescasbon/PyVCF.Lo primero que hay que hacer es clonar este repositorio desde el enlace
.git
(que requiere que tengamos instalada la herramientagit
). Esta clonación debería descargar una carpetaPyVCF
;cd
hasta allí y usarls
para ver el contenido:A continuación, necesitamos instalarlo, esto configurará el módulo y sus archivos necesarios, y los colocará en una carpeta oculta llamada
.local
dentro de su directorio principal ( Python también busca allí módulos y paquetes por defecto). Para introducirlo en.local
en tu propio directorio home en lugar de usar una instalación en todo el sistema (que quizás no tengas permiso para hacer), tenemos que agregar—user
al comando que ejecuta el scriptsetup.py
:Una vez hecho esto, vuelve a tu directorio original, y entonces puedes activar el intérprete interactivo de Python, cargar el módulo y ver su API:
(Lamentablemente, la documentación de la API en el paquete en sí no es tan buena, la versión en línea en http://pyvcf.rtfd.org es mejor, específicamente en la introducción).
- Escribe un programa llamado
vcf_module_use.py
que importe este módulovcf
y lo utilice para contar e imprimir cuántas líneas entrio.sample.vcf
tienen un alelo de referencia de“A
”
. (Tenga en cuenta que debe importar y usar el módulo, en lugar de analizar cada línea usted mismo).
- Gran parte de esta información también está disponible en línea en http://docs.python.org.
- Los módulos y paquetes de Python no necesitan estar instalados en todo el sistema o estar presentes en el mismo directorio que el programa que los usa. La variable de entorno
$PYTHONPATH
enumera los directorios que se buscan por módulos y paquetes, a los que cada usuario es libre de agregar sus propias rutas de búsqueda. - El módulo de gráficos
tortuga
es una forma divertida de entender y visualizar procesos computacionales (y paquetes similares están disponibles para muchos lenguajes de programación). Si bien este libro no es el lugar para tales exploraciones, el lector puede aprender más sobre ellas en el excelente libro de Jeffrey Elkner, Allen Downey y Chris Meyers, How to Think Like a Computer Scientist (Green Tea Press, 2002). Al momento de escribir este artículo, una versión del libro está disponible en línea en http://interactivepython.org/runestone/static/thinkcspy/index.html. - En algunos scripts de Python, puede encontrar una línea
si __name__ == '__main__':
. El código dentro de esta sentencia if se ejecutará, pero solo si el archivo se ejecuta como un script. Si se importa como módulo, el bloque no se ejecutará. Esto permite a los desarrolladores escribir archivos que pueden servir para ambos propósitos, y muchos desarrolladores lo incluyen por defecto para que las funciones de su script y las definiciones de clase puedan ser utilizadas en el futuro importando el archivo como un módulo. - Los paquetes también pueden contener otros archivos, como archivos que contienen conjuntos de datos específicos o incluso código escrito en otros idiomas como C. Por lo tanto, algunos módulos pueden necesitar ser instalados por procesos más complicados que simplemente descomprimirlos en la ubicación correcta.
- Es posible que ocasionalmente vea una línea como
de modulename import *
. Esto permite usar las funciones y otras definiciones dentro demodulename
sin prefijarlas conmodulename.
. Por ejemplo, podemosimportar matemáticas
y luego usarprint (math.sqrt (3.0))
, o podemosdesde math import *
y luegoprint (sqrt (3.0))
. Esto último generalmente se evita, porque si múltiples módulos contienen la misma función u otros nombres, entonces no es posible desambiguarlos. - Dependiendo del sistema en el que estés trabajando, es posible que ya tengas el paquete BioPython. Si no, puedes usar la utilidad
pip
install para instalarlo:pip install BioPython
, o si no tienes privilegios de administrador,pip install —user BioPython
. Algunas versiones anteriores de Python no vienen con la utilidadpip
, pero esta se puede instalar con una herramienta más antigua:easy_install pip
oeasy_install —user pip
. - Es seguro decir que Python no está “meramente” orientado a objetos, está muy orientado a objetos.