{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "El otro día estuvimos hablando de la [biblioteca `collections`](http://pybonacci.org/2016/05/08/joyitas-en-la-stdlib-collections/), una joya dentro de la librería estándar. Hoy vamos a hablar de una nueva biblioteca que se incluyó en la versión 3.4 de CPython llamada [`pathlib`](https://docs.python.org/3/library/pathlib.html). \n", "\n", "**Solo python 3, actualízate!!!**\n", "\n", "Esta biblioteca nos da la posibilidad de usar clases para trabajar con las rutas del sistema de ficheros con una serie de métodos muy interesantes.\n", "\n", "## Algunas utilidades para configurar el problema\n", "\n", "Vamos a crear un par de funciones que nos permiten crear y borrar un directorio de pruebas para poder reproducir el ejemplo de forma sencilla:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import os\n", "import glob\n", "import shutil\n", "from random import randint, choice, seed\n", "from string import ascii_letters\n", "\n", "# función que nos crea un directorio de prueba en\n", "# el mismo directorio del notebook\n", "def crea_directorio():\n", " seed(1)\n", " base = os.path.join(os.path.curdir,\n", " 'pybonacci_probando_pathlib')\n", " os.makedirs(base, exist_ok = True)\n", "\n", " for i in range(0, randint(3, 5)):\n", " folder = ''.join([choice(ascii_letters) for _ in range(4)])\n", " path = os.path.join(base, folder)\n", " os.makedirs(path, exist_ok = True)\n", " for j in range(0, randint(2, 5)):\n", " ext = choice(['.txt', '.py', '.html'])\n", " name = ''.join([choice(ascii_letters) for _ in range(randint(5, 10))])\n", " filename = name + ext\n", " path2 = os.path.join(path, filename)\n", " open(path2, 'w').close()\n", "\n", "# Función que nos permite hacer limpieza \n", "def borra_directorio():\n", " base = os.path.join(os.path.curdir,\n", " 'pybonacci_probando_pathlib')\n", " shutil.rmtree(base + os.path.sep)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si ahora ejecutamos la función `crea_directorio`:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "crea_directorio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nos debería quedar una estructura parecida a lo siguiente:\n", "\n", " pybonacci_probando_pathlib/\n", " ├── KZWe\n", " │   ├── CrUZoLgubb.txt\n", " │   ├── IayRnBUbHo.txt\n", " │   ├── WCEPyYng.txt\n", " │   └── yBMWX.py\n", " ├── WCFJ\n", " │   ├── GBGQmtsLFG.html\n", " │   ├── PglOUshVv.py\n", " │   └── RoWDsb.py\n", " └── zLcE\n", " ├── AQlxJSXR.html\n", " ├── fCQGgXk.html\n", " └── xFUbEctT.html\n", "\n", "\n", "## Ejemplo usando lo disponible hasta hace poco\n", "\n", "Pensemos en un problema que consiste en identificar todos los ficheros *.py* disponibles en determinada ruta y dejarlos en una nueva carpeta, que llamaremos *python*, todos juntos eliminándolos de la carpeta original en la que se encuentren.\n", "\n", "De la forma antigua esto podría ser así:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Suponemos que ya has creado los directorios y ficheros\n", "# de prueba usando crea_directorio()\n", "\n", "# recolectamos todos los ficheros *.py con sus rutas\n", "base = os.path.join(os.path.curdir,\n", " 'pybonacci_probando_pathlib')\n", "ficheros_py = glob.glob(os.path.join(base, '**', '*.py'))\n", "\n", "# creamos la carpeta 'python' \n", "# dentro de 'pybonacci_probando_pathlib'\n", "os.makedirs(os.path.join(base, 'python'), exist_ok = True)\n", "\n", "# y movemos los ficheros a la nueva carpeta 'python'\n", "for f in ficheros_py:\n", " fich = f.split(os.path.sep)[-1]\n", " shutil.move(f, os.path.join(base, 'python'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nuestra nueva estructura de ficheros debería ser la siguiente:\n", "\n", " pybonacci_probando_pathlib/\n", " ├── KZWe\n", " │   ├── CrUZoLgubb.txt\n", " │   ├── IayRnBUbHo.txt\n", " │   └── WCEPyYng.txt\n", " ├── python\n", " │   ├── PglOUshVv.py\n", " │   ├── RoWDsb.py\n", " │   └── yBMWX.py\n", " ├── WCFJ\n", " │   └── GBGQmtsLFG.html\n", " └── zLcE\n", " ├── AQlxJSXR.html\n", " ├── fCQGgXk.html\n", " └── xFUbEctT.html\n", "\n", "En el anterior ejemplo hemos tenido que usar las bibliotecas `glob`, `os` y `shutil` para poder realizar una operación relativamente sencilla. Esto no es del todo deseable porque he de conocer tres librerías diferentes y mi cabeza no da para tanto.\n", "\n", "## Limpieza\n", "\n", "Me cargo la carpeta *pybonacci_probando_pathlib* para hacer un poco de limpieza:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [], "source": [ "borra_directorio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Y vuelvo a crear la estructura de ficheros inicial:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": true }, "outputs": [], "source": [ "crea_directorio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Después de la limpieza vamos a afrontar el problema usando `pathlib`.\n", "\n", "## El mismo ejemplo con `pathlib`\n", "\n", "Primero importamos la librería y, como bonus, creamos una función que hace lo mismo que la función `borra_directorio` pero usando `pathlib`, que llamaremos `borra_directorio_pathlib`:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "def borra_directorio_pathlib(path = None):\n", " if path is None:\n", " p = Path('.', 'pybonacci_probando_pathlib')\n", " else:\n", " p = path\n", " for i in p.iterdir():\n", " if i.is_dir():\n", " borra_directorio_pathlib(i)\n", " else:\n", " i.unlink()\n", " p.rmdir()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La anterior función con `shutil` es un poco más sencilla que con `pathlib`. Esto es lo único que hecho de menos en `pathlib`, algunas utilidades de `shutil` que vendrían muy bien de serie. Algo negativo tenía que tener.\n", "\n", "En la anterior función, `borra_directorio_pathlib`, podemos ver ya algunas cositas de `pathlib`.\n", "\n", "`p = Path('.', 'pybonacci_probando_pathlib')` nos crea una ruta que ahora es un objeto en lugar de una cadena. Dentro del bucle usamos el método [`iterdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.iterdir) que nos permite iterar sobre los directorios de la ruta definida en el objeto `p`. el iterador nos devuelve nuevos objetos que disponen de métodos como [`is_dir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_dir), que nos permite saber si una ruta se refiere a un directorio, o [`unlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink), que nos permite eliminar el fichero o enlace. Por último, una vez que no tenemos ficheros dentro del directorio definido en `p` podemos usar el método [`rmdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.rmdir) para eliminar la carpeta.\n", "\n", "Ahora veamos cómo realizar lo mismo que antes usando `pathlib`, es decir, mover los ficheros *.py* a la carpeta *python* que hemos de crear." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# recolectamos todos los ficheros *.py con sus rutas\n", "p = Path('.', 'pybonacci_probando_pathlib')\n", "ficheros_py = p.glob('**/*.py')\n", "\n", "# creamos la carpeta 'python' dentro de 'pybonacci_probando_pathlib'\n", "(p / 'python').mkdir(mode = 0o777, exist_ok = True)\n", "\n", "# y copiamos los ficheros a la nueva carpeta 'python'\n", "for f in ficheros_py:\n", " target = p / 'python' / f.name\n", " f.rename(target)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nuevamente, nuestra estructura de ficheros debería ser la misma que antes:\n", "\n", " pybonacci_probando_pathlib/\n", " ├── KZWe\n", " │ ├── CrUZoLgubb.txt\n", " │ ├── IayRnBUbHo.txt\n", " │ └── WCEPyYng.txt\n", " ├── python\n", " │ ├── PglOUshVv.py\n", " │ ├── RoWDsb.py\n", " │ └── yBMWX.py\n", " ├── WCFJ\n", " │ └── GBGQmtsLFG.html\n", " └── zLcE\n", " ├── AQlxJSXR.html\n", " ├── fCQGgXk.html\n", " └── xFUbEctT.html\n", "\n", "Repasemos el código anterior:\n", "Hemos creado un objeto ruta `p` tal como habíamos visto antes en la función `borra_directorio_pathlib`. Este objeto ahora dispone de un método [`glob`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob) que nos devuelve un iterador con lo que le pidamos, en este caso, todos los ficheros con extensión *.py*. En la línea `(p / 'python').mkdir(mode = 0o777, exist_ok = True)` podemos ver el uso de `/` como operador para instancias de `Path`. El primer paréntesis nos devuelve una nueva instancia de `Path` que dispone del método [`mkdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.mkdir) que hace loque todos esperáis. Como `ficheros_py` era un iterador podemos usarlo en el bucle obteniendo nuevas instancias de `Path` con las rutas de los ficheros python que queremos mover. en la línea donde se define `target` hacemos uso del atributo [`name`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.name),que nos devuelve la última parte de la ruta. Por último, el fichero con extensión *.py* definido en el `Path` `f` lo renombramos a una nueva ruta, definida en `target`.\n", "\n", "Y todo esto usando una única librería!!!\n", "\n", "Echadle un ojo a la [documentación oficial](https://docs.python.org/3/library/pathlib.html) para descubrir otras cositas interesantes.\n", "\n", "Si además de usar una única librería usamos parte de la funcionalidad de `shutil` tenemos una pareja muy potente, `pathlib` + `shutil`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Limpieza II\n", "\n", "Y para terminar, limpiamos nuestra estructura de ficheros pero usando ahora la función `borra_directorio_pathlib` que habíamos creado pero no usado aún:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": true }, "outputs": [], "source": [ "borra_directorio_pathlib()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Enjoy!!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }