From e81137474f102d71fe0b25307bcf88810c1b2d43 Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Wed, 4 Mar 2020 13:41:02 +0100 Subject: New arrangement for multilanguage and metadata support --- src/06_ejec_mod.md | 271 ----------------------------------------------------- 1 file changed, 271 deletions(-) delete mode 100644 src/06_ejec_mod.md (limited to 'src/06_ejec_mod.md') diff --git a/src/06_ejec_mod.md b/src/06_ejec_mod.md deleted file mode 100644 index 0c54f4f..0000000 --- a/src/06_ejec_mod.md +++ /dev/null @@ -1,271 +0,0 @@ -# Módulos y ejecución - -Hasta ahora, has ejecutado el código en la REPL y de vez en cuando has usado -`F5` en IDLE para ejecutar. Aunque te ha permitido salir del paso, necesitas -saber más en detalle cómo funciona la ejecución para empezar a hacer tus -programas. Además, es absurdo que te pelees contra todo, hay que saber qué -batallas librar, así que necesitarás aprender a importar código realizado por -otras personas para poder centrarte en lo que más te interesa: resolver tu -problema. - -Este capítulo trata ambas cosas, que están muy relacionadas, y sirve como -trampolín para el siguiente, la instalación de nuevos paquetes y la gestión de -dependencias, y los posteriores sobre librerías interesantes que te facilitarán -el desarrollo de tus proyectos. - -Este capítulo ya te capacita casi al cien por cien para la programación aunque -aún no hemos trabajado su utilidad, pero seguro que alguna idea se te habrá -ocurrido, si no no estarías leyendo este documento. - -## Terminología: módulos y paquetes - -En python a cualquier fichero de código (extensión `.py`) se le denomina -*módulo* (*module*). A cualquier directorio que contenga módulos de código -python y un fichero llamado `__init__.py`, que puede estar vacío, se le -denomina *paquete* (*package*). El uso del fichero `__init__.py` permite que -python busque módulos dentro del directorio. - -Piensa en los paquetes como grupos de módulos que incluso pueden anidarse con -subpaquetes. En la documentación oficial verás en más de una ocasión que se -trata a los paquetes como si fueran módulos, y tiene cierto sentido, porque, -normalmente, existe un módulo principal que permite el acceso a un subpaquete. -Así que, principalmente estás trabajando con un único módulo de un paquete, que -contiene subpaquetes a los que este módulo hace referencia. Aunque pueda sonar -algo enrevesado, no te preocupes por ahora: los módulos son ficheros únicos y -los paquetes conjuntos de ellos. - -## Ejecución - -Ya conoces un par de maneras de ejecutar tus módulos de python. Usar la REPL, -introduciéndole el código que quieres ejecutar, llamar a la función «ejecutar -módulo» de IDLE con la tecla `F5` e incluso llamar a la shell de sistema con un -comando similar a este: - -``` bash -python mi_archivo.py -``` - -Normalmente, los ficheros de python se ejecutan de este último modo en -producción, mientras que los dos anteriores son más usados a la hora de -desarrollar. Realmente son métodos similares, en todos ellos el intérprete -accede al fichero o contenido que recibe y ejecuta las líneas una a una. - -Existe también una opción adicional muy usada que sirve para ejecutar módulos -que el sistema sea capaz de encontrar por sí mismo, en lugar de indicarle la -ruta, se le puede indicar simplemente el nombre del módulo usando la opción -`-m`: - -``` bash -python -m nombre_de_modulo -``` - -## Importación y *namespaces* - -Anteriormente hemos pasado sobre la sintaxis de la importación de forma muy -superficial pero tampoco es mucho más compleja a lo que ha aparecido. La -sentencia `import` permite importar diferentes módulos a nuestro programa como -en el siguiente ejemplo: - -``` python ->>> import datetime ->>> datetime - -``` - -Han pasado, sin embargo, muchas cosas interesantes en el ejemplo. En primer -lugar, python ha buscado y encontrado el módulo `datetime` en el sistema y, en -segundo lugar, ha creado un objeto módulo llamado `datetime` que atesora todas -las definiciones globales del módulo `datetime`. - -Empezando por el final, python usa lo que se conoce como *namespace* de forma -muy extendida. Los *namespaces*, de nombre (*name*) y espacio (*space*), son -una herramienta para separar contextos ampliamente usada. Los objetos, en -realidad, son una caso de *namespace* ya que cuando se llama a un método se le -dice cuál es el contexto de la llamada, es decir: al método de qué objeto se -llama. - -Para los módulos el proceso es el mismo. La sentencia `import` trae un módulo -al programa pero lo esconde tras su *namespace*, de este modo, para acceder a -algo definido en el recién importado módulo es necesario indicarle el nombre de -éste de la siguiente manera: - -``` python ->>> import datetime ->>> datetime.date.today() -datetime.date(2019, 12, 3) -``` - -En el ejemplo se accede a la clase `date` dentro del módulo `datetime`, y se -lanza su función `today`, que indica el día de hoy. Como puedes apreciar, el -operador `.` se utiliza del mismo modo que en las clases y objetos, y en -realidad es difícil saber cuándo se está accediendo a una clase y cuándo a un -módulo, aunque tampoco es necesario saberlo. - -Para no tener que escribir el nombre del módulo completo, existe otra versión -de la sentencia `import` que tiene un comportamiento muy similar: - -``` python ->>> import datetime as dt ->>> dt.date.today() -datetime.date(2019, 12, 3) -``` - -En este ejemplo, se ha cambiado el nombre del módulo a uno más corto decidido -por el programador, `dt`. El funcionamiento es el mismo, simplemente se ha -cambiado el nombre para simplificar. Este cambio de nombre también es útil -cuando se va a importar un módulo cuyo nombre es igual que alguna otra -definición. Cambiando el nombre se evitan colisiones. - -Existen versiones además, que permiten importar únicamente las funciones y -clases seleccionadas, pero que las añaden al *namespace* actual, para evitar -tener que usar el prefijo. - -``` python ->>> from datetime import date ->>> date.today() -datetime.date(2019, 12, 3) -``` - -En este último ejemplo, se trae la clase `date` al contexto actual. También -existe la posibilidad de importar más de una definición del módulo, usando la -coma para separarlas, o todo lo que el módulo exponga mediante el símbolo `*`. -Es peligroso, sin embargo, traer definiciones al namespace actual de forma -descuidada, sobre todo con la última opción, porque, es posible que se repitan -nombres por accidente y se pisen definiciones. Los namespaces se inventan con -el fin de separar las definiciones y evitar colisiones de este tipo. - -### Búsqueda - -Una vez descrito cómo se interactúa con los módulos importados, es necesario -describir dónde se buscan estos módulos. - -Los módulos se buscan en los siguientes lugares: - -1. El directorio del fichero ejecutado o el directorio de trabajo de la REPL -2. Los directorios indicados en el entorno. -3. La configuración por defecto (depende de la instalación) - -Esto significa que si guardas un archivo de python en IDLE y guardas otro más -en el mismo directorio con el nombre `modulo.py` podrás importarlo usando -`import modulo` en el primero, ya que comparten directorio. Lo mismo ocurre con -los paquetes, crear un directorio con nombre `paquete` y añadirle un fichero -vacío llamado `init.py` te permitirá hacer `import paquete`. Si añadieras más -módulos dentro del paquete, podrías importar cada uno de ellos mediante -`paquete.modulo`. - -> NOTA: Los nombres de los ficheros deben coincidir con el el nombre del módulo -> más la extensión `.py`. En el caso de los directorios, saltar a un -> subdirectorio implica acceder a un paquete, por lo que se añadirá un punto -> (`.`). - -El primer punto sirve para facilitar que organices tu proyecto de python en -varios módulos, separando así la funcionalidad en diferentes archivos. - -Los últimos dos puntos son los que permiten a python encontrar su librería -estándar y los módulos de sistema. El tercero depende de la instalación y del -formato de ésta: si python está instalado como portable no será igual que si se -instala en el sistema del modo habitual. El segundo punto también puede variar -de un sistema a otro, pero en resumen se trata de varias variables de entorno -de sistema que le indican a python dónde buscar (normalmente toman el nombre -`PYTHONPATH`, pero no es siempre así). El segundo punto puede alterarse de modo -que en función de lo que se le indique, se puede pedir a python que busque los -módulos en un lugar u otro. - -Estos lugares de búsqueda se pueden mostrar de la siguiente manera: - -``` python ->>> import sys ->>> print(sys.path) -[ '', - '/usr/lib/python36.zip', - '/usr/lib/python3.6', - '/usr/lib/python3.6/lib-dynload', - '/usr/local/lib/python3.6/dist-packages', - '/usr/lib/python3/dist-packages'] -``` - -En función del sistema en el que te encuentres y la configuración que tengas, -python mostrará diferente resultado. - -Rescatando un ejemplo previo: - -``` python ->>> import datetime ->>> datetime - -``` - -Ahora entiendes por qué es capaz de encontrar `datetime` en -`/usr/lib/python3.6`, carpeta listada en `sys.path`, bajo el nombre -`datetime.py`. - -## Ejecución vs Importación: `__main__` *guard* - -A la hora de importar un módulo, python procesa el contenido de éste ya que -necesita definir las funciones, clases, valores, etc. a exportar: ejecuta el -módulo. - -Python define una forma de separar la funcionalidad del código de sus -definiciones con el fin de poder crear código cuyas definiciones sean -reutilizables mediante la importación en otro módulo, sin que tenga ninguna -funcionalidad cuando esto ocurra, pero habilitando que tenga funcionalidades -cuando sea llamado directamente. - -Un ejemplo de uso de esto puede ser un módulo de acceso a ficheros, por -ejemplo, que visualice el contenido del fichero cuando se llame de forma -directa pero que cuando se importe únicamente aporte las funciones de lectura y -escritura sin leer y mostrar ningún fichero. - -Para que los módulos puedan tener esta doble vida, python define la variable -`__name__` que representa en qué nivel del *scope* se está ejecutando el módulo -actual. La variable `__name__` toma el valor del nombre del módulo cuando éste -está siendo importado y el valor `__main__` cuando ha sido llamado de forma -directa o está siendo ejecutado en la REPL. `__main__` es el *scope* global de -los programas, por lo que cuando algo se declara en él, implica que es el -programa principal. - -Para poder diferenciar cuándo se ha ejecutado un módulo de forma directa y -cuando se ha importado se utiliza lo que se conoce como `__main__` *guard*: - -``` python -if __name__ == "__main__": - # Este bloque sólo se ejecuta cuando el módulo es el principal -``` - -Aunque igual es un poco incómodo de entender de primeras, encontrarás esta -estructura en casi cualquier módulo de código python. Se utiliza -constantemente, incluso para los casos en los que no se pretende que el código -pueda importarse. Es una buena práctica incluir el *guard* para separar la -ejecución de las definiciones, de este modo, quien quiera saber cuál es la -funcionalidad del módulo tendrá mucho más fácil la búsqueda. - -Puedes leer más sobre este tema en la documentación de python[^main-guard]. - -[^main-guard]: - - -Siguiendo este concepto, también existe el un estándar de nomenclatura de -ficheros. El nombre `__main__.py` hace referencia al fichero que contiene el -código que se incluiría dentro del *guard* y será el fichero que python buscará -ejecutar siempre que se le pida ejecutar un paquete o un directorio sin -especificar qué módulo debe lanzar. Por ejemplo, ejecutar `python .`[^dot] en -la shell de sistema es equivalente a ejecutar `python __main__.py`. - -[^dot]: `.` significa directorio actual en cualquiera de los sistemas - operativos comunes. - - -## Lo que has aprendido - -En este capítulo corto has aprendido lo necesario sobre importación de módulos -y ejecución de código. Conocer en detalle el patrón de búsqueda de módulos de -python es primordial para evitar problemas en el futuro y organizar los -proyectos de forma elegante. - -Además, el `__main__` *guard* era una de las últimas convenciones de uso común -que quedaban por explicar y una vez vista ya eres capaz de leer proyectos de -código fuente que te encuentres por ahí sin demasiados problemas. - -Los próximos capítulos, basándose en lo aprendido en éste, te mostrarán cómo -instalar nuevas dependencias y cómo preparar tu propio código para que pueda -ser instalado de forma limpia y elegante. -- cgit v1.2.3