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.
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.
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'
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:
Type: list
String Form:['', 'a = 3', 'a = 1', "get_ipython().magic('pinfo In')", "get_ipython().magic('pinfo In')"]
Length: 5
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:
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)
2
Out
{1: '/home/kiko/.config/ipython/profile_default', 2: '/home/kiko/.config/ipython/profile_default/history.sqlite'}
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
2
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)
2
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)
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)
El input de la celda 11 es: for celda in In: print(celda)
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]
'for celda in In:\n print(celda)'
_ih[11]
'for celda in In:\n print(celda)'
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:
%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
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:
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')
.
_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
%dhist
Directory history (kept in _dh) 0: /home/kiko/pyprojs/ipython-master/nb 1: /home/kiko/pyprojs 2: /home/kiko/pyprojs/ipython-master/nb
_dh
['/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]
'/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.