Pybonacci

Computación Científica con Python en castellano

¡Disponible Curso de Introducción a Python para científicos e ingenieros!

with 4 comments

¡Ya está disponible en vídeo el Curso de Introducción a Python para científicos e ingenieros que grabé en la Escuela Politécnica Superior de Alicante!

Los videotutoriales de este curso se han diseñado para que sea práctico, con ejemplos extraídos de asignaturas comunes en carreras científicas y de ingeniería.

Tienes la información completa sobre el curso de Python científico en la web de CAChemE http://cacheme.org/curso-online-python-cientifico-ingenieros/ (enlace corto http://bit.ly/curso-python).

Desde aquí muchísimas gracias tanto a la Universidad como a ellos por haber hecho posible que exista por fin un curso de estas características en español. No podemos hacer nada con las limitaciones del profesor pero aun así esperamos que lo disfrutéis :)

Además de tener todos los vídeos en YouTube, tienes los notebooks y todo el material del curso en nuestro GitHub y los vídeos en iTunes para iPad y otros dispositivos.

Temario

  • Introducción a la sintaxis de Python
  • Uso del Notebook de IPython
  • Introducción a NumPy
  • Representación gráfica con matplotlib
  • Análisis numérico con SciPy
  • Introducción a la depuración con pdb, testing y buenas prácticas

No dudéis en hacernos llegar todos vuestros comentarios y sugerencias, y ¡a difundir!

¡Un saludo! :D

Written by Juanlu001

18 de marzo de 2014 at 16:00

Publicado en Tutoriales

Haz una librería de la que me pueda fiar… o no la hagas

with 8 comments

Cuando creas código, debes poder confiar en algunas cosas, como el sistema operativo o el intérprete de Python. Si no, sería casi imposible encontrar un error. Por suerte casi todo lo que usamos es lo suficientemente maduro para tener la certeza de que si algo no funciona la causa está en nuestro código.

¿Qué pasa con las librerías de terceros? También tenemos que confiar en ellas y para ello hay que comprobar los parámetros de entrada. Esta entrada te propone que hagamos juntos una reflexión sobre este tema.

Verificando parámetros

Una librería es un fragmento de código muy delicado. No está pensada para ser utilizada por un usuario final a través de una interfaz que limite y controle lo que el usuario pueda hacer. Una librería está pensada para ser parte de otro código y para permitir que sean programadores o software craftmen quienes la utilicen a veces de manera muy ingeniosa.

Para que una librería tenga éxito y los desarrolladores la utilicen es fundamental que sea de fiar. Una librería podrá hacer su trabajo mejor o peor, incluso puede no ser capaz de trabajar en determinadas circunstancias, pero si obtenemos un error de nuestro código siempre debe estar claro si el error es interno de la propia librería y hemos encontrado un bug o nos topamos con una limitación documentada, si el error es de nuestro código que usa mal la librería o si el error es de nuestro código y no tiene nada que ver con la librería.

Para conseguir esto es fundamental tratar los parámetros que reciben los métodos o funciones de la librería adecuadamente. Tomemos como ejemplo una sencilla librerías de funciones matemáticas básicas: potencia, exponencial, divisores, mínimo común divisor, máximo común múltiplo, etc.

Algunas de estas funciones no aceptan números negativos,  ¿tenemos que comprobar todos los métodos? .

Si un parámetro negativo producirá una excepción más adelante, ¿para qué esperar?. Una buena alternativa es comprobar el parámetro justo al comienzo de la llama al método o función y lanzar una excepción. Por ejemplo, imagina esta implementación básica de la función de Fibonacci.

def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

¿Cuál es la diferencia entre verificar el valor del parámetro o dejar que el código falle dónde tenga que fallar? Compruébalo tú mismo viendo cuál de los dos siguientes errores es más claro.

Sin comprobar valores de entrada.

Error en función de Fibonacci sin comprobar parámetros

Comprobando valores de entrada.

res02

Si comprobamos todos los parámetros de entrada, estaremos escribiendo una librería sólida que ayuda a sus usuarios a escribir buen código. Pero también podemos estar repitiendo muchas veces el código de comprobación y repetir código siempre es malo.

Por suerte tenemos varias alternativas. Además, resolver este problema nos da una indicación de nuestro nivel de programación. Algunas opciones son:

  • Creamos un método interno auxiliar que compruebe los parámetros. Todos nuestros métodos llamarían a dicho método (así está implementada las clases de java.collection, por ejemplo).
  • Creamos un decorador que compruebe los parámetros (los decoradores no son tan difíciles como parece).
  • Utilizamos alguna librería de comparaciones, como sure o expects.

Si se te ocurren más soluciones ponlas como comentario de este artículo. No te preocupes si crees que no son mejores que las que ya hemos visto, porque siempre nos servirá para aprender un poco más.

Unidades con magnitudes

Otro tema interesante es cómo trabajar con magnitudes que tienen una unidad asociada. Por ejemplo, pensemos en la temperatura. Si nos dan estas temperaturas: 10, 283 y 50, ya podemos cruzar los dedos para que el termómetro se haya estropeado.

Pero si nos dicen que la temperatura es 10 grados centígrados, 283 grados kelvin y 50 grados Fahrenheit descubrimos que, en verdad, estamos hablando de la misma temperatura y que mejor vamos abrigados o pasaremos frio.

A Python y a nuestras librerías le pasa lo mismo, a veces necesita las unidades porque un un número no les dice nada. Pero Python no tiene ningún tipo de dato nativo para empresar magnitudes y unidades. Lo mejor es crearlo nosotros mismos con una clase. ¿Cómo podría ser una clase temperara (llamada temp para abreviar)?

  • Podríamos crear métodos estáticos, por ejemplo  tem.kelvin(50) o temp.celcius(-4) que comprobaran que una temperatura es válida en el rango de sus unidades.
  • Podríamos crear métodos de conversión para obtener la temperatura en la magnitud que deseemos, por ejemplo si creamos temp.kelvin(283) y vemos el valor de temp. celcius () obtenemos un correcto 10.
  • Podríamos sobrecargar los operadores para comparar y operar con temperaturas más intuitivamente.
  • Y podríamos buscar si alguien ya lo ha hecho por nosotros.

Nos evitaríamos, por ejemplo el problema de mezclar distintas magnitudes, ya que la clase  verificaría por nosotros que las operaciones se realicen siempre con la misma unidad.

Bola extra

Existe una técnica de diseño 7 programación conocida como diseño por contratos (design by contract) en las que, para cada operación se especifican sus precondiciones, post-condiciones e invariantes y luego se implementan en el código.

Esta técnica alcanzó popularidad a finales de los 90 por el desastre el cohete Ariane 5, el cual se podría haber evitado especificando los contratos de las operaciones.

Ariadne 5, o lo que queda.

Ariane 5, o lo que queda.

Si os manejáis por el mundo Java, podéis ver esta técnica en aplicación por ejemplo en java.collection, aunque luego no implementéis todas las precondiciones, poscondiciones e invariante sí es una buena idea pensar en los contratos de vuestras librerías. Además, también sirve de ayuda para diseñar casos de prueba.

Written by javierjus

18 de marzo de 2014 at 9:14

Publicado en Tutoriales

Etiquetado con ,

Presentando brythonmagic

with one comment

En esta entrada os voy a enseñar una extensión que he creado que funciona sobre el IPython notebook, es decir, está pensada para funcionar en un navegador. Si no sabéis muy bien que son las ‘cell magics’ (no sé muy bien como traducir esto, podría ser algo así como magia que aplica sobre celdas) le podéis echar un ojo a esta entrada donde ya creamos alguna función mágica (‘line magics’) de ayuda. De todas formas, entre mis planes está el hacer una parte de la serie ’157 cosas de IPython…’ que hable sobre las ‘cell magics’. No han salido nuevos capítulos ya que estoy viendo como evoluciona IPython 2.0.0 y adaptar las entradas a la nueva versión de IPython para que no se queden desfasadas ya de salida…

Bueno, a lo que vamos,… ¿Qué es eso de brythonmagic? Brythonmagic pretende ayudar a los programadores Python a crear un puente entre el mundo de la programación científica en Python y el de la visualización interactiva en javascript a través del IPython notebook y con la ayuda de Brython, una implementación de Python3 hecha en javascript y en Python y que funciona en el lado del cliente (navegador web). De esa forma, escribiendo en Python/subconjunto de Python/seudoPython/… (i.e., escribiendo en Brython) podemos acceder al navegador y a interesantes librerías javascript que funcionan en el navegador.

Brython es un proyecto que ya empieza a madurar y nos permite hacer cosas bastante interesantes que hasta ahora estaban vetadas para los pythonistas. Mediante brythonmagic he llevado las capacidades de Brython al notebook de IPython creando una ‘cell magic’, %%brython, que permite ejecutar código Brython dentro del navegador y nos permite manejar documentos HTML, acceder al DOM,…

El funcionamiento es muy sencillo, añades la extensión al notebook de IPython, añades las librerías de Brython que vayas a necesitar y después ya puedes ejecutar celdas de código Brython dentro del notebook y ver el resultado al momento.

A día de hoy permite una serie de opciones:

  • Obtener, mediante pantalla, el html que se usa para el output de la celda de código
  • Indicar que librerías javascript vamos a usar en la celda de código
  • Usar datos de Python creados en otras celdas y que queramos usar en la celda de código Brython
  • Indicar el nombre del contenedor (elemento HTML div) de la salida.

En el siguiente vídeo podéis echarle un vistazo rápido a lo que se puede hacer con brythonmagic. En el vídeo, entre otras cosas, accedemos a las librerías d3js (gráficos interactivos) y openlayers (mapas interactivos):

El notebook usado en el vídeo está en el repositorio por si lo queréis ejecutar de forma local.

El repo de Brython lo tenéis aquí.

Además de para usarse para lo comentado anteriormente también lo podéis usar para probar cosas de Brython en un entorno amigable como el notebook de IPython (resaltado de sintáxis, autocompletado, formateo del código, comentarios en Markdown,…), es decir, si eres un ‘frontender’ y quieres probar Brython puedes usar brythonmagic y IPython para ello :-).

Saludos.

P.D.: Se agradecen ideas para mejorar brythonmagic.

Written by Kiko

3 de marzo de 2014 at 6:00

Publicado en Noticias

Etiquetado con , , , , , ,

¿Cómo separar parte real e imaginaria en SymPy?

leave a comment »

Andaba yo preguntándome esta mañana, a falta (¡sorprendentemente!) de dudas de los lectores:

Si tengo un número complejo en SymPy, ¿cómo puedo separar la parte real y la parte imaginaria? Y ya puestos, ¿puedo separar también el módulo y el argumento?

Si utilizamos el tipo complex de Python el resultado es correcto pero puede no ser demasiado vistoso:

$ isympy
In [1]: (1j + 1) / (5 - 2j)
Out[1]: (0.10344827586206896+0.24137931034482757j)

Queremos usar las capacidades simbólicas de SymPy. En SymPy, como se indica en el tutorial, los complejos se declaran de esta manera:

In [2]: (1 * I + 1) / (5 - 2 * I)
Out[2]: 
 1 + ⅈ 
───────
5 - 2⋅ⅈ

Y ya tenemos un objeto de SymPy con toda su potencia (que además se imprime bonito). Para extraer la parte real e imaginaria podemos usar las funciones re e im o el método as_real_imag.

In [3]: a = (1 * I + 1) / (5 - 2 * I)

In [4]: re(a)
Out[4]: 3/29

In [5]: im(a)
Out[5]: 7/29

In [6]: a.as_real_imag()
Out[6]: (3/29, 7/29)

Estos métodos extraen la parte real y la imaginaria pero «pierdo» el número original. Para reescribir el número separando parte real e imaginaria lo mejor es emplear el método expand(complex=True):

In [9]: a.expand(complex=True)
Out[9]: 
3    7⋅ⅈ
── + ───
29    29

Esto ya es otra cosa ;)

Leer el resto de esta entrada »

Written by Juanlu001

4 de febrero de 2014 at 10:26

Publicado en Preguntas

Etiquetado con , ,

MicroEntradas: numpy.vectorize

with 2 comments

Hoy vamos a ver numpy.vectorize que, como la documentación oficial indica, sirve para ‘vectorizar’ funciones que solo aceptan escalares como entrada. La entrada que podemos meter es una lista de objetos o un ‘numpyarray’ y nos devolverá como resultado un ‘numpyarray’.

No os engañéis, normalmente, cuando hablamos de vectorizar pensamos en varios órdenes de magnitud de mejora en el rendimiento pero eso no es lo que hace esta función :-). Podéis pensar en esta función como algo parecido a usar map.

Por ejemplo, la función abs solo acepta un entero o un float. Por ejemplo, lo siguiente:


print(abs(-3))

Nos devolvería el valor 3. Hasta ahí bien. Pero si queremos hacer lo siguiente:


print(abs([-3, -2]))

Nos devolverá un TypeError: bad operand type for abs(): 'list'.

Podríamos usar map para ello pero no obtendríamos un ‘numpyarray’ como salida y en Python 3 devuelve un iterador que para transformarlo a, por ejemplo, una lista o un ‘numpyarray’ debemos añadir un paso más, la conversión a lista (en Python 2 devuelve directamente una lista) o dos pasos, la conversión a lista y esta a ‘numpyarray’.

Vamos a ver cómo podemos ‘vectorizar’ la función abs sin usar map y usando numpy.vectorize (antes deberéis importar numpy como np):


vectabs = np.vectorize(abs)

que podemos usar de la siguiente forma:


print(vectabs([-3, -2]))

Et voilà. ya tenemos lo que queríamos. Veamos como va el rendimiento de lo que acabamos de hacer:


In [1]: import numpy as np

In [2]: kk = np.random.randn(1000000)

In [3]: timeit np.abs(kk)
100 loops, best of 3: 4.13 ms per loop

In [4]: vectabs = np.vectorize(abs)

In [5]: timeit vectabs(kk)
10 loops, best of 3: 87.6 ms per loop

In [6]: timeit np.array([abs(i) for i in kk])
1 loops, best of 3: 241 ms per loop

La última opción es equivalente a hacer np.array(list(map(abs, kk))) en tiempo. La versión vectorizada, vectabs, es 21 veces más lenta que la que podemos obtener usando numpy.abs por lo que no tendríamos una gran ganancia pero, sin embargo, vemos que es casi tres veces más rápida que la versión usando map (o una ‘list comprehension’) por lo que algo ganariamos respecto a Python 3 puro :-).

Como apuntes finales, si sabéis de alguna función en CPython que no existe equivalente en numpy y la necesitáis usar quizá podéis obtener una ganancia usando numpy.vectorize. Si tenéis que escribir la función vosotros, escribidla pensando en operaciones vectorizadas usando ‘numpyarrays’ y no os hará falta usar numpy.vectorize.

Como punto final. recordad que np.vectorize no es más que un decorador por lo que lo siguiente sería perfectamente válido:


@np.vectorize
def mi_funcion(*args):
    ...

Saludos.

[Editado: corrección de algún bug, disculpas!!]

Written by Kiko

31 de enero de 2014 at 6:30

157 cosas de IPython que no sabías y nunca preguntaste (II)

with 2 comments

En la primera entrega vimos como usar la ayuda de IPython y como personalizar nuestras propias funciones mágicas de ayuda.

En esta segunda entrega vamos a hablar del uso de la historia dentro de IPython.

[Inciso: Os recomiendo que veáis lo que viene a continuación en el notebook o en nbviewer. WordPress.com cada vez me parece más limitado para mostrar el contenido que mostramos en este blog]

IPython guarda la historia de los comandos que se usan en cada línea/celda de cada sesión bajo un determinado perfil. Esta información se guarda en una base de datos sqlite. Por defecto, se guardan el inicio y fin de sesión, los comandos usados en cada sesión y algunos metadatos más. IPython también se puede configurar para que almacene los outputs.

Historia

La base de datos con la historia se guarda en la carpeta que obtenéis haciendo lo siguiente:


from IPython.utils import path
path.locate_profile()


'/home/kiko/.config/ipython/profile_default'

Y la base de datos se guardará en esa carpeta con el nombre history.sqlite.

Una forma alternativa de obtener la ruta a la historia bajo el perfil actual es usando lo siguiente:


get_ipython().history_manager.hist_file


'/home/kiko/.config/ipython/profile_default/history.sqlite'

Acceso a determinadas celdas de la historia

Para reusar comandos ya usados podemos hacer uso de la historia que guarda IPython en cada sesión.

  • Podemos usar las teclas de cursor hacia arriba o hacia abajo para recorrer los últimos comandos usados (esto no funciona en el notebook pero sí en la consola de IPython).
  • Si escribimos algo en la línea de comandos y pulsamos el cursor hacia arriba nos mostrará solo lo que comience por lo ya escrito en la línea de comandos (nuevamente, esto no funciona en el notebook pero sí en la consola de IPython).
  • En las sesiones interactivas, el input y el output se guarda en las variables In y Out. Poniendo el índice de la línea usada nos volverá a ejecutar esa línea, en el caso de que usemos la variable In o nos mostrará el output en caso de que usemos la variable Out. In es una lista mientras que Out es un diccionario. En el caso de que no haya output para un número de línea nos dará un KeyError. Por ejemplo, veamos las siguientes celdas de código:

In?

Nos mostrará en pantalla lo siguiente (esto puede variar en función de lo que hayáis escrito previamente en la consola o el notebook, lo siguiente solo sirve como ejemplo):


Type:       list
String Form:['', "get_ipython().magic('pinfo In')", "get_ipython().magic('pinfo In')"]
 Length:     3
 Docstring:
list() -> new empty list
list(iterable) -> new list initialized from iterable's items

Mientras que si hacemos lo mismo para Out obtendremos la siguiente info (esto puede variar en función de lo que hayáis escrito previamente en la consola o el notebook, lo siguiente solo sirve como ejemplo):


Type:       dict
String Form:{}
Length:     0
Docstring:
dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
d = {}
for k, v in iterable:
    d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

Si ahora hacemos lo siguiente:


a = 2
print(a)

Nos mostrará un 2 en pantalla pero no lo guarda en el Output puesto que solo es visualización. Por tanto, lo siguiente:


Out

Nos mostrará (esto puede variar en función de lo que hayáis escrito previamente en la consola o el notebook, lo siguiente solo sirve como ejemplo):


{1: '/home/kiko/.config/ipython/profile_default',
 2: '/home/kiko/.config/ipython/profile_default'}

Vemos que en Out se encuentra lo que hemos obtenido en las primeras celdas de código. Si ahora queremos ver otro output podemos hacer lo siguiente:


a

Y, como no lo hemos mostrado con un print, se añade al Output.


Out


{1: '/home/kiko/.config/ipython/profile_default',
 2: '/home/kiko/.config/ipython/profile_default/history.sqlite',
 7: 2}

Vemos que ya tenemos algún valor para el Out y ya podremos acceder a ese valor por si lo quisiéramos usar en alguna otra celda. Por ejemplo, de la siguiente forma:


b = Out[7]
print(b)

Nos mostraría 2 en pantalla.

Como el In siempre lo tendremos, en lugar de ser un diccionario es una lista y podemos acceder al valor de la celda usando el índice de la misma. Por ejemplo:


for celda in In:
    print(celda)

Nos mostraría lo siguiente (o algo aproximado a lo siguiente):


from IPython.utils import path
path.locate_profile()
get_ipython().history_manager.hist_file
get_ipython().magic('pinfo In')
a = 2
print(a)
Out
a
Out
b = Out[7]
print(b)
for celda in In:
    print(celda)

También podemos acceder a los tres últimos outputs de las tres últimas celdas usando _, __, ___, que nos mostrará el output de la última, penúltima o antepenúltima celdas usadas, respectivamente.


print('antepenúltima:\n', ___, '\n\n')
print('penúltima:\n', __, '\n\n')
print('última:\n', _, '\n\n')


antepenúltima:
 /home/kiko/.config/ipython/profile_default 


penúltima:
 /home/kiko/.config/ipython/profile_default/history.sqlite 


última:
 2 

Si queremos acceder a los inputs de las últimas celdas podemos usar algo parecido pero de la siguiente forma, _i, _ii o _iii para la última, penúltima o antepenúltima celda de input:


print('antepenúltima:\n', _iii, '\n\n')
print('penúltima:\n', _ii, '\n\n')
print('última:\n', _i, '\n\n')


antepenúltima:
 print(b) 


penúltima:
 for celda in In:
    print(celda) 


última:
print('antepenúltima:\n', ___, '\n\n')
print('penúltima:\n', __, '\n\n')
print('última:\n', _, '\n\n') 

Análogamente a lo visto anteriormente, podemos usar _n o _in para mostrar, respectivamente, el output o el input de la celda n. Por ejemplo, para ver el input y el output de la celda anterior (en este caso sería la 11) podemos hacer lo siguiente:


print('El input de la celda 11 es:')
print(_i11)

Que nos mostrará:


El input de la celda 11 es:
for celda in In:
    print(celda)

Y para el output:


print('Te he engañado. No existe output para la celda 11')
print('Si intentas acceder al valor _11 obtendrás un NameError ya que no existe la variable')
print('pero te puedo enseñar el de la celda 7:')
print(_7)


Te he engañado. No existe output para la celda 11
Si intentas acceder al valor _11 obtendrás un NameError ya que no existe la variable
pero te puedo enseñar el de la celda 7:
2

Lo anterior es equivalente a usar In[n] o Out[n]. Una tercera alternativa, además, sería usar _ih[n] para los inputs y _oh[n] para los outputs.


In[11]

Mostrará:


'for celda in In:\n    print(celda)'

Mientras que:


_ih[11]

Nos mostrará lo mismo:


'for celda in In:\n    print(celda)'

Acceso a bloques de historia

Para acceder a toda la historia de la sesión actual podemos usar las funciones mágicas %history o %hist, que es un alias.

Podemos obtener toda la historia o solo una porción. Por ejemplo, el siguiente comando nos mostrará la historia desde la celda 1 a la 10 en la sesión actual:


%hist 1-10


from IPython.utils import path
path.locate_profile()
get_ipython().history_manager.hist_file
In?
a = 2
print(a)
Out
a
Out
b = Out[7]
print(b)

Si, además de acceder a las celdas 1 a 10, queremos acceder a celdas sueltas podemos usar la siguiente notación para acceder a las celdas 12 y 14 (además de a las 10 primeras).


%hist 1-10 12 14


from IPython.utils import path
path.locate_profile()
get_ipython().history_manager.hist_file
In?
a = 2
print(a)
Out
a
Out
b = Out[7]
print(b)
print('antepenúltima:\n', ___, '\n\n')
print('penúltima:\n', __, '\n\n')
print('última:\n', _, '\n\n')
print('El input de la celda 11 es:')
print(_i11)

Si ahora queremos acceder a todas las celdas donde hayamos usado, por ejemplo, un comando que incluya ‘a = 1′ podemos hacer uso de la opción -g (similar a grep) de la siguiente forma (la salida dependerá de vuestra historia):


%hist -g a = 1


29/129: a = 1
29/133: a = 1
29/136: a = 1.1
29/138: a = 1
29/142: a = 1.1
29/145: a = 1
29/147: a = 1
29/149: a = 1
29/151: a = 1
51/7: a = 1
185/1: a = 1
187/19: %hist -g a = 1
187/20: %hist -g [a = 1]
187/21: %hist -g a = 1
188/20: %hist -g a = 1
189/2: a = 1
190/1: a = 1
190/3: a = 1
190/4: a = 1
190/10: a = 1
201/1:
code = """

def hello():
    if a = 1:
        print(a)
    elseif a =2:
        print(a)
    else:
        print('kk')
    return None

"""
201/3:
code = """

def hello():
    if a = 1:
        print(a)
    elif a =2:
        print(a)
    else:
        print('kk')
    return None

"""
201/5:
code = """

def hello():
    if a = 1:
        print(a)
    elif a = 2:
        print(a)
    else:
        print('kk')
    return None

"""
201/14:
a = 1
b = exec(code)
201/15:
a = 1
b = exec(code)
print(b)
201/16:
a = 1
b = exec(code)
print(c)
201/17:
a = 1
b = exec(code)
print(b)
201/43: def a():a = 1;return a
201/45: def a():a = 1;b=2return a
201/46: def a():a = 1;b=2;return a,b
201/81:
a = 1
code = """

def hello():
    if a == 1:
        print('a=1')
    elif a == 2:
        print('a=2')
    else:
        if a == 3:
            print('a=3')
    return None

"""
201/85:
a = 1
code = """

def hello():
    if a == 1:
        print('a=1')
    elif a == 2:
        print('a=2')
    else:
        if a == 3:
            print('a=3')
    return None

"""
201/93:
a = 1
code = """

def hello():
    if a == 1:
        return('a=1')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None

"""
201/135:
a = 2
code = """
a = 1
def hello():
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None

hello()

"""
201/157:
a = 2
code = """
a = 1
def hello():
    """Hola"""
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None
    
class A():
    """Hola"""
    def __init__(self):
        "Hola"
    
    def _kk(self):
        'Adios'

hello()
help(A)

"""
201/158:
a = 2
code = """
a = 1
def hello():
    '''Hola'''
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None
    
class A():
    """Hola"""
    def __init__(self):
        "Hola"
    
    def _kk(self):
        'Adios'

hello()
help(A)

"""
201/159:
a = 2
code = """
a = 1
def hello():
    '''Hola'''
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None
    
class A():
    "Hola"
    def __init__(self):
        "Hola"
    
    def _kk(self):
        'Adios'

hello()
help(A)

"""
201/174:
code = """
a = 1
def hello():
    '''
    Hola
    '''
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None
    
class A():
    "Hola"
    def __init__(self):
        "Hola"
    
    def _kk(self):
        'Adios'

hello()
help(A)

"""
201/219:
code = """
a = 1
def hello():
    '''
    Hola
    '''
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None

class A():
    "Hola"
    def __init__(self):
        "Hola"

    def _kk(self):
        'Adios'

print(hello())
help(A)

"""
201/230:
code = """
a = 1
def hello():
    '''
    Hola
    '''
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None

class A():
    "Hola"
    def __init__(self):
        "Hola"

    def _kk(self):
        'Adios'
        pass

print(hello())
help(A)

"""
201/233:
code = """
a = 1
def hello():
    '''
    Hola
    '''
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None

class A():
    "Hola"
    def __init__(self):
        "Hola"

    def _kk(self):
        'Adios'
        pass

print(hello())
help(A)

"""
201/237:
code = """
a = 1
def hello():
    '''
    Hola
    '''
    # esto es un comentario de mierda
    if a == 1:
    # esto es otro comentario
        return('a=1 # esto no sería un comentario')
    elif a == 2:
        return('a=2')
    else:
        if a == 3:
            return('a=3')
    return None

class A():
    "Hola"
    def __init__(self):
        "Hola"
        pass

    def _kk(self):
        'Adios'
        pass

print(hello())
help(A)

"""
202/2: a = 1
202/3: a = 1
202/4: a = 1
202/5:
a = 1
print(a)
214/20: %hist -g a = 1
  20: %hist -g a = 1

[Gracias a la historia sé que escribo demasiado código estúpido... :-(]

Pero esta busqueda no se restringe a la historia de la sesión actual sino que buscará en toda la historia almacenada por IPython bajo el perfil que estemos usando. El anterior output indica la sesión, el número de línea/celda de esa sesión y el código usado en esa línea/celda con la siguiente notación:

Sesión/celda: Código_introducido_en_la_celda

En este caso, podéis ver que en la última línea no se indica el número de sesión puesto que se refiere a la sesión actual:

Si usamos la opción -o también obtendremos la historia con el output incluido. Podéis ver el siguiente ejemplo para ver como funciona:


%hist -o


from IPython.utils import path
path.locate_profile()
'/home/kiko/.config/ipython/profile_default'
get_ipython().history_manager.hist_file
'/home/kiko/.config/ipython/profile_default/history.sqlite'
In?
a = 2
print(a)
Out
{1: '/home/kiko/.config/ipython/profile_default',
 2: '/home/kiko/.config/ipython/profile_default/history.sqlite'}
a
2
Out
{1: '/home/kiko/.config/ipython/profile_default',
 2: '/home/kiko/.config/ipython/profile_default/history.sqlite',
 7: 2}
b = Out[7]
print(b)
for celda in In:
    print(celda)
print('antepenúltima:\n', ___, '\n\n')
print('penúltima:\n', __, '\n\n')
print('última:\n', _, '\n\n')
print('antepenúltima:\n', _iii, '\n\n')
print('penúltima:\n', _ii, '\n\n')
print('última:\n', _i, '\n\n')
print('El input de la celda 11 es:')
print(_i11)
print('Te he engañado. No existe output para la celda 11')
print('Si intentas acceder al valor _11 obtendrás un NameError ya que no existe la variable')
print('pero te puedo enseñar el de la celda 7:')
print(_7)
In[11]
'for celda in In:\n    print(celda)'
_ih[11]
'for celda in In:\n    print(celda)'
%hist 1-10
%hist 1-10 12 14
%hist -g a = 1
%hist -o

Otra cosa interesante es la opción -p, que coloca un prompt delante de cada línea de la historia que se muestra. Esto puede ser útil para, por ejemplo, escribir doctests.

En el siguiente ejemplo vamos a usar la opción -p junto con la opción -o:


%hist -po 1-10


>>> from IPython.utils import path
... path.locate_profile()
...
'/home/kiko/.config/ipython/profile_default'
>>> get_ipython().history_manager.hist_file
'/home/kiko/.config/ipython/profile_default/history.sqlite'
>>> In?
>>> a = 2
>>> print(a)
>>> Out
{1: '/home/kiko/.config/ipython/profile_default',
 2: '/home/kiko/.config/ipython/profile_default/history.sqlite'}
>>> a
2
>>> Out
{1: '/home/kiko/.config/ipython/profile_default',
 2: '/home/kiko/.config/ipython/profile_default/history.sqlite',
 7: 2}
>>> b = Out[7]
>>> print(b)

Si queremos guardar la historia o parte de la historia en un fichero para, por ejemplo, los doctests, podemos usar la opción -f.

Con la siguiente línea de código vamos a guardar el input, el output y vamos a colocar la línea del prompt de las 10 primeras celdas en un fichero llamado kk.txt:


%hist 1-10 -pof kk.txt

Si queremos acceder a la historia de una sesión anterior podemos usar lo siguiente:


%hist ~1/1-10


from IPython.utils import path
path.locate_profile()
get_ipython().history_manager.hist_file
In?
a = 2
print(a)
Out
a
Out
b = Out[7]
print(b)

De esta forma accederemos a las 10 primeras líneas de la sesión anterior. Si queremos acceder a las 10 primeras líneas de la penúltima sesión podemos hacer:


%hist ~2/1-10


from IPython.utils import path
path.locate_profile()
get_ipython().history_manager.hist_file
In?
a = 2
print(a)
Out
a
Out
b = Out[9]

Si, además, queréis numerar las celdas usadas podéis usar la opción -n:


%hist ~2/1-10 -n


213/1:
from IPython.utils import path
path.locate_profile()
213/2: get_ipython().history_manager.hist_file
213/3: In?
213/4: a = 2
213/5: print(a)
213/6: Out
213/7: a
213/8: Out
213/9: b = Out[9]

Algunos de los comandos usados no son aceptados por un intérprete Python cualquiera, como por ejemplo los comandos mágicos que empiezan por %. Por ello, podemos obtener los comandos ya traducidos a código Python ejecutable usando la opción -t de la historia:


%hist 1-10 -t


from IPython.utils import path
path.locate_profile()
get_ipython().history_manager.hist_file
get_ipython().magic('pinfo In')
a = 2
print(a)
Out
a
Out
b = Out[7]
print(b)

En la tercera línea podéis ver que en lugar de escribir %pinfo In ha escrito get_ipython().magic(‘pinfo In’).

Acceso a la historia de los directorios usados

_dh (también podemos usar %dhist) nos da información de los directorios recorridos. Por ejemplo, voy a recorrer varios directorios y después veremos la historia de los directorios recorridos:


cd /home/kiko/pyprojs


/home/kiko/pyprojs


pwd


'/home/kiko/pyprojs'


cd /home/kiko/pyprojs/ipython-master/nb/


/home/kiko/pyprojs/ipython-master/nb

Si ahora escribimos:


%dhist


Directory history (kept in _dh)
0: /home/kiko/pyprojs/ipython-master/nb
1: /home/kiko/pyprojs
2: /home/kiko/pyprojs/ipython-master/nb

O algo, más o menos, equivalente:


_dh

En este caso nos devuelve una lista:


['/home/kiko/pyprojs/ipython-master/nb',
 '/home/kiko/pyprojs',
 '/home/kiko/pyprojs/ipython-master/nb']

Si solo quiero saber el directorio del que partí en la sesión de IPython en la que me encuentro puedo hacer lo siguiente:


_dh[0]

Y obtengo:


'/home/kiko/pyprojs/ipython-master/nb'

Y esto es todo de momento. Podéis combinar muchas cosas de las vistas aquí con cosas como %macro, %edit, %pastebin,… Si da tiempo, algo muy caro últimamente, hablaremos sobre algunas cosas que se me ocurren en próximas entregas.

Saludos y hasta la próxima entrega.

P.D.: Si veis alguna errata podéis usar los comentarios o mandar algún commit al repositorio de los notebooks.

Written by Kiko

29 de enero de 2014 at 6:30

Publicado en Artículos, Tutoriales

Etiquetado con , ,

157 cosas de IPython que no sabías y nunca preguntaste (I)

with 3 comments

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án ahí y son tremendamente útiles una vez que las conoces y las incluyes en tu flujo de trabajo.

Inicialmente se iba a llamar ’305928 cosas sobre IPython que no sabías (o sí) y que nunca tuviste tiempo de preguntar o de leer en la documentación o de leer en el código o nunca te hicieron falta o te estabas tomando unas cañas o estabas haciendo otras cosas más seductoras e inconfesables o {0}’ pero me pareció un título un poco largo. Finalmente lo he reducido a 157 y pico, más o menos, para vuestra salud mental y la mia. El nombre está inspirado de forma muy libre en el nombre de la charla dada por Victor Terrón en la PyConES 2013 (¿que no la has visto? venga, a hacer los deberes, rápido. Y después vuelve por aquí).

Primero de todo, [mode taliban ON] se escribe IPython, las dos primeras en mayúscula y no iPython o ipython o aipaizon o IPhyton (sic) [mode taliban OFF]. Por tanto, recordadlo si no queréis que la vena de la frente de Mathias Bussonier se hinche.

Todos sabéis lo que es IPhyton (:-)), si no es así le puedes echar un vistazo a la documentación que está en la página oficial o visitar este vídeo que preparó JuanLu sobre el notebook. Si, aun así, sois tan vagos como yo y no queréis ver nada de lo anterior os cuento, como brevísimo resumen, que IPython es una consola interactiva con super poderes y magia negra incluida.

Ayuda

Ayuda estándar de IPython

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ímbolos de interrogación. Si se usa un solo símbolo de interrogación se obtiene información general del objeto mientras que si se usan dos símbolos de interrogación se puede acceder a la implementación 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ía estándar y programada en Python.


from calendar import calendar

?calendar

Nos daría la siguiente información (os saltará una ventana en la parte inferior del navegador):

Type:       method
String Form:<bound method TextCalendar.formatyear of <calendar.TextCalendar object at 0xb6b6082c>>
File:       /usr/local/lib/python3.3/calendar.py
Definition: calendar(self, theyear, w=2, l=1, c=6, m=3)
Docstring:  Returns a year's calendar as a multi-line string.
Class Docstring:
method(function, instance)

Create a bound instance method object.

[Nota] El comando anterior es equivalente a hacer calendar?,
y también es equivalente a usar %pinfo calendar.

Si ahora usamos el doble signo de interrogación obtendremos información mucho más detallada como el fichero en el que se encuentra la función y el código usado para implementarla, entre otras cosas:


??calendar

La siguiente información saldrá en una ventana en la parte inferior del navegador

Type:       method
String Form:<bound method TextCalendar.formatyear of <calendar.TextCalendar object at 0xb6b6082c>>
File:       /usr/local/lib/python3.3/calendar.py
Definition: calendar(self, theyear, w=2, l=1, c=6, m=3)
Source:
    def formatyear(self, theyear, w=2, l=1, c=6, m=3):
        """
        Returns a year's calendar as a multi-line string.
        """
        w = max(2, w)
        l = max(1, l)
        c = max(2, c)
        colwidth = (w + 1) * 7 - 1
        v = []
        a = v.append
        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
        a('\n'*l)
        header = self.formatweekheader(w)
        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
            # months in this row
            months = range(m*i+1, min(m*(i+1)+1, 13))
            a('\n'*l)
            names = (self.formatmonthname(theyear, k, colwidth, False)
                     for k in months)
            a(formatstring(names, colwidth, c).rstrip())
            a('\n'*l)
            headers = (header for k in months)
            a(formatstring(headers, colwidth, c).rstrip())
            a('\n'*l)
            # max number of weeks for this row
            height = max(len(cal) for cal in row)
            for j in range(height):
                weeks = []
                 for cal in row:
                    if j >= len(cal):
                        weeks.append('')
                    else:
                        weeks.append(self.formatweek(cal[j], w))
                a(formatstring(weeks, colwidth, c).rstrip())
                a('\n' * l)
        return ''.join(v)

Class Docstring:
method(function, instance)

Create a bound instance method object.

[Nota] El comando anterior es equivalente a hacer calendar??
y también es equivalente a usar %pinfo2 calendar.

Si ahora usamos la ayuda usual disponible, función help() de la siguiente forma help(calendar), veremos que la información es bastante más escueta que la obtenida mediante IPython.


help(calendar)

El resultado será:

Help on method formatyear in module calendar:

formatyear(self, theyear, w=2, l=1, c=6, m=3) method of calendar.TextCalendar instance
Returns a year's calendar as a multi-line string.

Uso de wildcards o comodines

Con el signo de interrogación también podemos usar wildcards para obtener todos los objetos que cumplen el criterio. Por ejemplo, nos acordamos que el otro día usamos una función 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:


ca*?

Y IPython nos dará lo siguiente:

calendar
callable

Esto no es excesivamente útil ya que IPython ofrece autocompletado con la tecla de tabulación y llegaríamos al mismo resultado de forma sencilla. Pero, ¿y si nos acordamos que el objeto usado terminaba por ar en lugar de empezar por ca? En este caso, el autocompletado no nos resultaría de mucha ayuda. Pero podríamos usar lo siguiente:


*ar?

Lo anterior nos daría lo siguiente:

calendar

Pero no, no era lo que buscaba. En realidad estaba buscando un objeto que contenía ar. Lo podemos buscar de la siguiente forma.


*ar*?

Lo anterior nos daría lo siguiente:

BytesWarning
DeprecationWarning
FutureWarning
ImportWarning
KeyboardInterrupt
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
Warning
bytearray
calendar
vars

Algunas funciones mágicas de ayuda

También le podéis echar un ojo a %pdef, %pdoc, %psource o %pfile para obtener información de diverso tipo sobre el objeto de turno.

Creación de nuestras propias funciones mágicas de ayuda

La ayuda del notebook sale en una zona inferior de la ventana del navegador. En general me resulta más incómoda que si se imprimiese como un ouput estándar dentro del navegador mismo. Con la siguiente receta, un poco modificada por mí, podemos hacer que determinada información salga inline:

Receta hecha por jiffyclub

## Importamos distintas funciones del módulo oinspect de IPython.
## Muchas de estas funciones son un wrapper sobre las funciones del
##   módulo inspect de la stdlib.
from IPython.core.oinspect import (getsource, getdoc,
                                   find_file, find_source_lines)
## Importamos lo siguiente para mostrar la información en pantalla
from IPython.display import display, HTML
## Importamos lo siguiente para convertir nuestra ayuda a magig functions
from IPython.core.magic import Magics, magics_class, line_magic
## Los siguientes imports serán usados para resaltar la sintáxis del código
##   fuente.
## Es necesario tener instalada la librería pygments.
from pygments import highlight
from pygments.lexers import PythonLexer
from pygments.formatters import HtmlFormatter

## Llamamos a la InteractiveShell y obtenemos el namespace
##   del usuario (user_ns) que es un diccionario con los
##   objetos disponibles
ip = get_ipython()
my_ns = ip.user_ns

@magics_class
class KikoMagic(Magics):
    def __init__(self, shell):
        super(KikoMagic, self).__init__(shell)

    @line_magic
    def kdoc(self, obj):
        """
        Retrieve the info of an object and display this info in an output.
        """
        if obj in my_ns.keys():
            print("Doc info for {}:\n".format(obj))
            print(getdoc(my_ns[obj]))
        else:
            print("There is no info for {}".format(obj))

    @line_magic
    def kfile(self, obj):
        """
        Retrieve the file where the object is implemented.
        """
        if obj in my_ns.keys():
            print("{} implemented in file:\n".format(obj))
            print(find_file(my_ns[obj]))
        else:
            print("We can't not find the file for {}".format(obj))

    @line_magic
    def ksourceline(self, obj):
        """
        Retrieve the first line in the source file where
        the object is implemented.
        """
        if obj in my_ns.keys():
            print("The implementation of {}".format(obj))
            print("starts at line {}".format(find_source_lines(my_ns[obj])))
            print("in file {}".format(find_file(my_ns[obj])))
        else:
            print("We can't not find the file for {}".format(obj))

    @line_magic
    def ksource(self, obj):
        """
        Retrieve the info and the source of an object and
        display the info in an output.
        """
        formatter = HtmlFormatter(linenos=False, cssclass="source", nobackground=True)
        template = """</pre>
<style><!--
{}
--></style>
<pre>{}"""

 src = getsource(my_ns[obj])
 html = highlight(src, PythonLexer(), formatter)
 css = formatter.get_style_defs()
 display(HTML(template.format(css,html)))

 @line_magic
 def khelp(self, obj):
 self.kdoc(obj)
 print("")
 self.ksourceline(obj)
 print("")
 self.ksource(obj)

## Registramos las nuevas funciones mágicas para que estén
## disponibles en el nb.
ip.register_magics(KikoMagic)

Dos de las funciones mágicas solo funcionarán 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ódigo html de forma correcta. Ahora usamos las nuevas funciones mágicas que acabamos de crear:

  • kdoc

%kdoc calendar

Que nos devolverá:

Doc info for calendar:

Returns a year's calendar as a multi-line string.
  • kfile

%kfile calendar

Que nos devolverá:

calendar implemented in file: /usr/local/lib/python3.3/calendar.py
  • ksourceline

%ksourceline calendar

que nos devolverá:

The implementation of calendar starts at line 334 in file /usr/local/lib/python3.3/calendar.py
  • ksource

%ksource calendar

que nos devolverá:

    def formatyear(self, theyear, w=2, l=1, c=6, m=3):
        """
        Returns a year's calendar as a multi-line string.
        """
        w = max(2, w)
        l = max(1, l)
        c = max(2, c)
        colwidth = (w + 1) * 7 - 1
        v = []
        a = v.append
        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
        a('\n'*l)
        header = self.formatweekheader(w)
        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
            # months in this row
            months = range(m*i+1, min(m*(i+1)+1, 13))
            a('\n'*l)
            names = (self.formatmonthname(theyear, k, colwidth, False)
                     for k in months)
            a(formatstring(names, colwidth, c).rstrip())
            a('\n'*l)
            headers = (header for k in months)
            a(formatstring(headers, colwidth, c).rstrip())
            a('\n'*l)
            # max number of weeks for this row
            height = max(len(cal) for cal in row)
            for j in range(height):
                weeks = []
                for cal in row:
                    if j >= len(cal):
                        weeks.append('')
                    else:
                        weeks.append(self.formatweek(cal[j], w))
                a(formatstring(weeks, colwidth, c).rstrip())
                a('\n' * l)
        return ''.join(v)
  • khelp

%khelp calendar

Que nos devolverá:

Doc info for calendar:

Returns a year's calendar as a multi-line string.

The implementation of calendar
starts at line 334
in file /usr/local/lib/python3.3/calendar.py

    def formatyear(self, theyear, w=2, l=1, c=6, m=3):
        """
        Returns a year's calendar as a multi-line string.
        """
        w = max(2, w)
        l = max(1, l)
        c = max(2, c)
        colwidth = (w + 1) * 7 - 1
        v = []
        a = v.append
        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
        a('\n'*l)
        header = self.formatweekheader(w)
        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
            # months in this row
            months = range(m*i+1, min(m*(i+1)+1, 13))
            a('\n'*l)
            names = (self.formatmonthname(theyear, k, colwidth, False)
                     for k in months)
            a(formatstring(names, colwidth, c).rstrip())
            a('\n'*l)
            headers = (header for k in months)
            a(formatstring(headers, colwidth, c).rstrip())
            a('\n'*l)
            # max number of weeks for this row
            height = max(len(cal) for cal in row)
            for j in range(height):
                weeks = []
                for cal in row:
                    if j >= len(cal):
                        weeks.append('')
                    else:
                        weeks.append(self.formatweek(cal[j], w))
                a(formatstring(weeks, colwidth, c).rstrip())
                a('\n' * l)
        return ''.join(v)

¡¡Eyyy, qué chulo!! Acabamos de personalizar un poco el notebook de IPython de forma muy sencilla.

Si solo queremos hacer una función mágica lo podemos hacer de forma un poco más sencilla que la vista anteriormente. En el siguiente código se muestra cómo (receta hecha por Brian Granger y que he actualizado a las últimas versiones de IPython para hacerla funcionar):


from IPython.core.oinspect import getdoc

ip = get_ipython()
my_ns = ip.user_ns

## Definimos la función que hace lo que queremos. La siguiente
##   función hace lo mismo que la función mágica kdoc que hemos
##   implementado anteriormente
def new_magic(obj):
    if obj in my_ns.keys():
        print(getdoc(my_ns[obj]))
    else:
        print("No info found for {}".format(obj))

## En la siguiente línea definimos la anterior función como
##   una 'line magic' que se llamará 'my_new_magic'
ip.register_magic_function(new_magic, 'line', "my_new_magic")

Ahora la vamos a hacer funcionar:


%my_new_magic calendar

que nos devolverá:

Returns a year's calendar as a multi-line string.

Resumen

Hemos visto como:

  • usar la ayuda de IPython, más avanzada que el uso de la función built-in help(),
  • uso de comodines (wildcards),
  • las funciones mágicas útiles para obtener ayuda de Python,
  • como crear nuestras propias funciones mágicas de ayuda (o de lo que queráis).

Si se os ocurre algo para completar esta información podéis hacer un pull request a nuestro repo de notebooks y actualizaremos la información.

Written by Kiko

24 de enero de 2014 at 6:30

Publicado en Artículos, Tutoriales

Etiquetado con , ,

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 1.105 seguidores

%d personas les gusta esto: