{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Voy a inaugurar una nueva serie de entradas dedicadas a IPython hablando de cosas que no son tan evidentes o que la gente no suele usar pero que est\u00e1n ah\u00ed y son tremendamente \u00fatiles una vez que las conoces y las incluyes en tu flujo de trabajo.\n", "\n", "Inicialmente se iba a llamar *'305928 cosas sobre IPython que no sab\u00edas (o s\u00ed) y que nunca tuviste tiempo de preguntar o de leer en la documentaci\u00f3n o de leer en el c\u00f3digo o nunca te hicieron falta o te estabas tomando unas ca\u00f1as o estabas haciendo otras cosas m\u00e1s seductoras e inconfesables o {0}'* pero me pareci\u00f3 un t\u00edtulo un poco largo. Finalmente lo he reducido a 157 y pico, m\u00e1s o menos, para vuestra salud mental y la mia. El nombre est\u00e1 inspirado de forma muy libre en el nombre de [la charla dada por Victor Terr\u00f3n en la PyConES 2013](http://www.youtube.com/watch?v=75IiA11j81k) (\u00bfque no la has visto? venga, a hacer los deberes, r\u00e1pido. Y despu\u00e9s vuelve por aqu\u00ed).\n", "\n", "Primero de todo, **[mode taliban ON]** se escribe IPython, las dos primeras en may\u00fascula y no iPython o ipython o aipaizon o IPhyton (sic) **[mode taliban OFF]**. Por tanto, recordadlo si no quer\u00e9is que la vena de la frente de [Mathias Bussonier](http://twitter.com/MBussonn) se hinche. \n", "\n", "Todos sab\u00e9is lo que es IPhyton (:-)), si no es as\u00ed le puedes echar un vistazo a la documentaci\u00f3n que est\u00e1 en la [p\u00e1gina oficial](http://www.ipython.org) o visitar este [v\u00eddeo](*************) que prepar\u00f3 JuanLu sobre el notebook. Si, aun as\u00ed, sois tan vagos como yo y no quer\u00e9is ver nada de lo anterior os cuento, como brev\u00edsimo resumen, que IPython es una consola interactiva con super poderes y magia negra incluida." ] }, { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Ayuda" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Ayuda est\u00e1ndar de IPython" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a empezar con cosas muy sencillas como el uso de la ayuda que ofrece IPython. Normalmente, para obtener ayuda de un objeto se usa el comando `help()`, en IPython se puede usar la ayuda usando s\u00edmbolos de interrogaci\u00f3n. Si se usa un solo s\u00edmbolo de interrogaci\u00f3n se obtiene informaci\u00f3n general del objeto mientras que si se usan dos s\u00edmbolos de interrogaci\u00f3n se puede acceder a la implementaci\u00f3n misma del objeto (solo en el caso de que haya sido programado en Python). Por ejemplo, veamos la ayuda del objeto `calendar` dentro de la biblioteca `calendar` disponible en la librer\u00eda est\u00e1ndar y programada en Python." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from calendar import calendar" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [ "?calendar" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nos dar\u00eda la siguiente informaci\u00f3n (os saltar\u00e1 una ventana en la parte inferior del navegador):\n", "\n", " Type: method\n", " String Form:>\n", " File: /usr/local/lib/python3.3/calendar.py\n", " Definition: calendar(self, theyear, w=2, l=1, c=6, m=3)\n", " Docstring: Returns a year's calendar as a multi-line string.\n", " Class Docstring:\n", " method(function, instance)\n", "\n", " Create a bound instance method object.\n", "\n", "**[Nota] El comando anterior es equivalente a hacer `calendar?` y tambi\u00e9n es equivalente a usar `%pinfo calendar`.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si ahora usamos el doble signo de interrogaci\u00f3n obtendremos informaci\u00f3n mucho m\u00e1s detallada como el fichero en el que se encuentra la funci\u00f3n y el c\u00f3digo usado para implementarla, entre otras cosas:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "??calendar" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "La siguiente informaci\u00f3n saldr\u00e1 en una ventana en la parte inferior del navegador\n", "\n", " Type: method\n", " String Form:>\n", " File: /usr/local/lib/python3.3/calendar.py\n", " Definition: calendar(self, theyear, w=2, l=1, c=6, m=3)\n", " Source:\n", " def formatyear(self, theyear, w=2, l=1, c=6, m=3):\n", " \"\"\"\n", " Returns a year's calendar as a multi-line string.\n", " \"\"\"\n", " w = max(2, w)\n", " l = max(1, l)\n", " c = max(2, c)\n", " colwidth = (w + 1) * 7 - 1\n", " v = []\n", " a = v.append\n", " a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())\n", " a('\\n'*l)\n", " header = self.formatweekheader(w)\n", " for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):\n", " # months in this row\n", " months = range(m*i+1, min(m*(i+1)+1, 13))\n", " a('\\n'*l)\n", " names = (self.formatmonthname(theyear, k, colwidth, False)\n", " for k in months)\n", " a(formatstring(names, colwidth, c).rstrip())\n", " a('\\n'*l)\n", " headers = (header for k in months)\n", " a(formatstring(headers, colwidth, c).rstrip())\n", " a('\\n'*l)\n", " # max number of weeks for this row\n", " height = max(len(cal) for cal in row)\n", " for j in range(height):\n", " weeks = []\n", " for cal in row:\n", " if j >= len(cal):\n", " weeks.append('')\n", " else:\n", " weeks.append(self.formatweek(cal[j], w))\n", " a(formatstring(weeks, colwidth, c).rstrip())\n", " a('\\n' * l)\n", " return ''.join(v)\n", "\n", " Class Docstring:\n", " method(function, instance)\n", "\n", " Create a bound instance method object.\n", "\n", "**[Nota] El comando anterior es equivalente a hacer `calendar??` y tambi\u00e9n es equivalente a usar `%pinfo2 calendar`.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si ahora usamos la ayuda usual disponible, funci\u00f3n `help()` de la siguiente forma `help(calendar)`, veremos que la informaci\u00f3n es bastante m\u00e1s escueta que la obtenida mediante IPython." ] }, { "cell_type": "code", "collapsed": false, "input": [ "help(calendar)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on method formatyear in module calendar:\n", "\n", "formatyear(self, theyear, w=2, l=1, c=6, m=3) method of calendar.TextCalendar instance\n", " Returns a year's calendar as a multi-line string.\n", "\n" ] } ], "prompt_number": 4 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Uso de *wildcards* o comodines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Con el signo de interrogaci\u00f3n tambi\u00e9n podemos usar *wildcards* para obtener todos los objetos que cumplen el criterio. Por ejemplo, nos acordamos que el otro d\u00eda usamos una funci\u00f3n que empezaba por *ca* pero no nos acordamos del nombre completo. Podemos buscar todos los objetos que se encuentran en el *namespace* de la siguiente forma:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ca*?" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Y IPython nos dar\u00e1 lo siguiente:\n", "\n", " calendar\n", " callable\n", " \n", "Esto no es excesivamente \u00fatil ya que IPython ofrece autocompletado con la tecla de tabulaci\u00f3n y llegar\u00edamos al mismo resultado de forma sencilla. Pero, \u00bfy si nos acordamos que el objeto usado terminaba por *ar* en lugar de empezar por *ca*? En este caso, el autocompletado no nos resultar\u00eda de mucha ayuda. Pero podr\u00edamos usar lo siguiente:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "*ar?" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lo anterior nos dar\u00eda lo siguiente:\n", "\n", " calendar" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pero no, no era lo que buscaba. En realidad estaba buscando un objeto que conten\u00eda `ar`. Lo podemos buscar de la siguiente forma." ] }, { "cell_type": "code", "collapsed": false, "input": [ "*ar*?" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lo anterior nos dar\u00eda lo siguiente:\n", "\n", " BytesWarning\n", " DeprecationWarning\n", " FutureWarning\n", " ImportWarning\n", " KeyboardInterrupt\n", " PendingDeprecationWarning\n", " ResourceWarning\n", " RuntimeWarning\n", " SyntaxWarning\n", " UnicodeWarning\n", " UserWarning\n", " Warning\n", " bytearray\n", " calendar\n", " vars" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Algunas funciones m\u00e1gicas de ayuda" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tambi\u00e9n le pod\u00e9is echar un ojo a `%pdef`, `%pdoc`, `%psource` o `%pfile` para obtener informaci\u00f3n de diverso tipo sobre el `objeto` de turno." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Creaci\u00f3n de nuestras propias funciones m\u00e1gicas de ayuda" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La ayuda del notebook sale en una zona inferior de la ventana del navegador. En general me resulta m\u00e1s inc\u00f3moda que si se imprimiese como un ouput est\u00e1ndar dentro del navegador mismo. Con la siguiente receta, un poco modificada por m\u00ed, podemos hacer que determinada informaci\u00f3n salga *inline*:\n", "\n", "[Receta hecha por jiffyclub](https://gist.github.com/jiffyclub/5385501)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "## Importamos distintas funciones del m\u00f3dulo oinspect de IPython.\n", "## Muchas de estas funciones son un wrapper sobre las funciones del \n", "## m\u00f3dulo inspect de la stdlib.\n", "from IPython.core.oinspect import (getsource, getdoc, \n", " find_file, find_source_lines)\n", "## Importamos lo siguiente para mostrar la informaci\u00f3n en pantalla\n", "from IPython.display import display, HTML\n", "## Importamos lo siguiente para convertir nuestra ayuda a magig functions\n", "from IPython.core.magic import Magics, magics_class, line_magic\n", "## Los siguientes imports ser\u00e1n usados para resaltar la sint\u00e1xis del c\u00f3digo\n", "## fuente.\n", "## Es necesario tener instalada la librer\u00eda pygments.\n", "from pygments import highlight\n", "from pygments.lexers import PythonLexer\n", "from pygments.formatters import HtmlFormatter\n", "\n", "## Llamamos a la InteractiveShell y obtenemos el namespace\n", "## del usuario (user_ns) que es un diccionario con los \n", "## objetos disponibles\n", "ip = get_ipython()\n", "my_ns = ip.user_ns\n", "\n", "@magics_class\n", "class KikoMagic(Magics): \n", " def __init__(self, shell):\n", " super(KikoMagic, self).__init__(shell)\n", "\n", " @line_magic\n", " def kdoc(self, obj):\n", " \"\"\"\n", " Retrieve the info of an object and display this info in an output.\n", " \"\"\"\n", " if obj in my_ns.keys():\n", " print(\"Doc info for {}:\\n\".format(obj))\n", " print(getdoc(my_ns[obj]))\n", " else:\n", " print(\"There is no info for {}\".format(obj))\n", " \n", " @line_magic\n", " def kfile(self, obj):\n", " \"\"\"\n", " Retrieve the file where the object is implemented.\n", " \"\"\"\n", " if obj in my_ns.keys():\n", " print(\"{} implemented in file:\\n\".format(obj))\n", " print(find_file(my_ns[obj]))\n", " else:\n", " print(\"We can't not find the file for {}\".format(obj))\n", " \n", " @line_magic\n", " def ksourceline(self, obj):\n", " \"\"\"\n", " Retrieve the first line in the source file where \n", " the object is implemented.\n", " \"\"\"\n", " if obj in my_ns.keys():\n", " print(\"The implementation of {}\".format(obj))\n", " print(\"starts at line {}\".format(find_source_lines(my_ns[obj])))\n", " print(\"in file {}\".format(find_file(my_ns[obj])))\n", " else:\n", " print(\"We can't not find the file for {}\".format(obj))\n", " \n", " @line_magic\n", " def ksource(self, obj):\n", " \"\"\"\n", " Retrieve the info and the source of an object and \n", " display the info in an output.\n", " \"\"\"\n", " formatter = HtmlFormatter(linenos=False, cssclass=\"source\", nobackground=True)\n", " template = \"\"\"{}\"\"\"\n", " \n", " src = getsource(my_ns[obj])\n", " html = highlight(src, PythonLexer(), formatter)\n", " css = formatter.get_style_defs()\n", " display(HTML(template.format(css,html)))\n", " \n", " @line_magic\n", " def khelp(self, obj):\n", " self.kdoc(obj)\n", " print(\"\")\n", " self.ksourceline(obj)\n", " print(\"\")\n", " self.ksource(obj)\n", "\n", "## Registramos las nuevas funciones m\u00e1gicas para que est\u00e9n\n", "## disponibles en el nb.\n", "ip.register_magics(KikoMagic)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dos de las funciones m\u00e1gicas solo funcionar\u00e1n en el notebook y no en la consola de IPython ya que estamos haciendo uso de `display(HTML(...))` y la *qtconsole*, por ejemplo, no es capaz de mostrar el c\u00f3digo html de forma correcta. Ahora usamos las nuevas funciones m\u00e1gicas que acabamos de crear:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%kdoc calendar" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Doc info for calendar:\n", "\n", "Returns a year's calendar as a multi-line string.\n" ] } ], "prompt_number": 11 }, { "cell_type": "code", "collapsed": false, "input": [ "%kfile calendar" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "calendar implemented in file:\n", "\n", "/usr/local/lib/python3.3/calendar.py\n" ] } ], "prompt_number": 12 }, { "cell_type": "code", "collapsed": false, "input": [ "%ksourceline calendar" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "The implementation of calendar\n", "starts at line 334\n", "in file /usr/local/lib/python3.3/calendar.py\n" ] } ], "prompt_number": 13 }, { "cell_type": "code", "collapsed": false, "input": [ "%ksource calendar" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "
    def formatyear(self, theyear, w=2, l=1, c=6, m=3):\n",
        "        """\n",
        "        Returns a year's calendar as a multi-line string.\n",
        "        """\n",
        "        w = max(2, w)\n",
        "        l = max(1, l)\n",
        "        c = max(2, c)\n",
        "        colwidth = (w + 1) * 7 - 1\n",
        "        v = []\n",
        "        a = v.append\n",
        "        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())\n",
        "        a('\\n'*l)\n",
        "        header = self.formatweekheader(w)\n",
        "        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):\n",
        "            # months in this row\n",
        "            months = range(m*i+1, min(m*(i+1)+1, 13))\n",
        "            a('\\n'*l)\n",
        "            names = (self.formatmonthname(theyear, k, colwidth, False)\n",
        "                     for k in months)\n",
        "            a(formatstring(names, colwidth, c).rstrip())\n",
        "            a('\\n'*l)\n",
        "            headers = (header for k in months)\n",
        "            a(formatstring(headers, colwidth, c).rstrip())\n",
        "            a('\\n'*l)\n",
        "            # max number of weeks for this row\n",
        "            height = max(len(cal) for cal in row)\n",
        "            for j in range(height):\n",
        "                weeks = []\n",
        "                for cal in row:\n",
        "                    if j >= len(cal):\n",
        "                        weeks.append('')\n",
        "                    else:\n",
        "                        weeks.append(self.formatweek(cal[j], w))\n",
        "                a(formatstring(weeks, colwidth, c).rstrip())\n",
        "                a('\\n' * l)\n",
        "        return ''.join(v)\n",
        "
\n" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": [ "%khelp calendar" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Doc info for calendar:\n", "\n", "Returns a year's calendar as a multi-line string.\n", "\n", "The implementation of calendar\n", "starts at line 334\n", "in file /usr/local/lib/python3.3/calendar.py\n", "\n" ] }, { "html": [ "
    def formatyear(self, theyear, w=2, l=1, c=6, m=3):\n",
        "        """\n",
        "        Returns a year's calendar as a multi-line string.\n",
        "        """\n",
        "        w = max(2, w)\n",
        "        l = max(1, l)\n",
        "        c = max(2, c)\n",
        "        colwidth = (w + 1) * 7 - 1\n",
        "        v = []\n",
        "        a = v.append\n",
        "        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())\n",
        "        a('\\n'*l)\n",
        "        header = self.formatweekheader(w)\n",
        "        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):\n",
        "            # months in this row\n",
        "            months = range(m*i+1, min(m*(i+1)+1, 13))\n",
        "            a('\\n'*l)\n",
        "            names = (self.formatmonthname(theyear, k, colwidth, False)\n",
        "                     for k in months)\n",
        "            a(formatstring(names, colwidth, c).rstrip())\n",
        "            a('\\n'*l)\n",
        "            headers = (header for k in months)\n",
        "            a(formatstring(headers, colwidth, c).rstrip())\n",
        "            a('\\n'*l)\n",
        "            # max number of weeks for this row\n",
        "            height = max(len(cal) for cal in row)\n",
        "            for j in range(height):\n",
        "                weeks = []\n",
        "                for cal in row:\n",
        "                    if j >= len(cal):\n",
        "                        weeks.append('')\n",
        "                    else:\n",
        "                        weeks.append(self.formatweek(cal[j], w))\n",
        "                a(formatstring(weeks, colwidth, c).rstrip())\n",
        "                a('\\n' * l)\n",
        "        return ''.join(v)\n",
        "
\n" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "\u00a1\u00a1Eyyy, qu\u00e9 chulo!! Acabamos de personalizar un poco el notebook de IPython de forma muy sencilla." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si solo queremos hacer una funci\u00f3n m\u00e1gica lo podemos hacer de forma un poco m\u00e1s sencilla que la vista anteriormente. En el siguiente c\u00f3digo se muestra c\u00f3mo ([receta hecha por Brian Granger](https://github.com/ipython/ipython/wiki/Cookbook:-Sending-built-in-help-to-the-pager) y que he actualizado a las \u00faltimas versiones de IPython para hacerla funcionar):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from IPython.core.oinspect import getdoc\n", "\n", "ip = get_ipython()\n", "my_ns = ip.user_ns\n", "\n", "## Definimos la funci\u00f3n que hace lo que queremos. La siguiente\n", "## funci\u00f3n hace lo mismo que la funci\u00f3n m\u00e1gica kdoc que hemos\n", "## implementado anteriormente\n", "def new_magic(obj):\n", " if obj in my_ns.keys():\n", " print(getdoc(my_ns[obj]))\n", " else:\n", " print(\"No info found for {}\".format(obj))\n", "\n", "## En la siguiente l\u00ednea definimos la anterior funci\u00f3n como\n", "## una 'line magic' que se llamar\u00e1 'my_new_magic'\n", "ip.register_magic_function(new_magic, 'line', \"my_new_magic\")" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 16 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ahora la vamos a hacer funcionar:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%my_new_magic calendar" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Returns a year's calendar as a multi-line string.\n" ] } ], "prompt_number": 17 }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Resumen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hemos visto como:\n", "\n", "* usar la ayuda de IPython, m\u00e1s avanzada que el uso de la funci\u00f3n built-in `help()`, \n", "\n", "* uso de comodines (*wildcards*), \n", "\n", "* las funciones m\u00e1gicas \u00fatiles para obtener ayuda de Python, \n", "\n", "* como crear nuestras propias funciones m\u00e1gicas de ayuda (o de lo que quer\u00e1is).\n", "\n", "Si se os ocurre algo para completar esta informaci\u00f3n pod\u00e9is hacer un pull request a [nuestro repo de notebooks](https://github.com/Pybonacci/notebooks) y actualizaremos la informaci\u00f3n." ] } ], "metadata": {} } ] }