Subsecciones


7. Entrada y salida

Hay varios modos de presentar la salida de un programa; se pueden imprimir datos en un modo legible a los humanos o escribirlos en un fichero para uso posterior. Este capítulo explora algunas de las posibilidades.


7.1 Formato de salida mejorado

Hasta ahora hemos encontrado dos modos de escribir valores: las sentencias de expresión y la sentencia print Hay un tercer modo, utilizar el método write() de los objetos fichero, donde se puede acceder al fichero de salida estándar es accesible mediante sys.stdout. Consulta la Referencia de las bibliotecas si deseas obtener información sobre el tema.

A menudo, querrás tener más control sobre el formato de la salida que escribir valores separados por espacios. Hay dos modos de dar formato a la salida: El primer modo es gestionar tú mismo las cadenas. Mediante corte y empalme de cadenas se puede generar cualquier formato. El módulo estándar string contiene operaciones útiles para ajustar cadenas a un ancho de columna dado. Esto se discutiré en breve. El segundo modo es utilizar el operador % con una cadena como argumento izquierdo. El operador % interpreta el argumento izquierdo como una cadena de formato estilo sprintf() de C, que ha de ser aplicado sobre el argumento derecho para devolver la cadena resultante del formato.

Queda una cuestión, por supuesto: ¿Cómo convertir valores a cadenas? Afortunadamente, Python tiene un modo de convertir cualquier valor a cadena: pasarle la función repr() o, simplemente, escribir el valor entre comillas simples invertidas (``). He aquí algunos ejemplos:

>>> x = 10 * 3.14
>>> y = 200*200
>>> s = 'El valor de "x" es ' + `x` + ' e "y" vale ' + `y` + '...'
>>> print s
El valor de "x" es 31.4 e "y" vale 40000...
>>> # Las comillas invertidas funcionan sobre otros tipos, además de los números:
... p = [x, y]
>>> ps = repr(p)
>>> ps
'[31.4, 40000]'
>>> # Convertir a cadena añade a las cadenas comillas y barras invertidas:
... hola = 'hola, mundo\n'
>>> cadhola = `hola`
>>> print cadhola
'hola, mundo\012'
>>> # El argumento de las comillas invertidas puede ser una tupla:
... `x, y, ('fiambre', 'huevos')`
"(31.4, 40000, ('fiambre', 'huevos'))"

He aquí dos modos de escribir una tabla de cuadrados y cubos:

>>> import string
>>> for x in range(1, 11):
...     print string.rjust(`x`, 2), string.rjust(`x*x`, 3),
...     # Observa la coma final de la línea anterior
...     print string.rjust(`x*x*x`, 4)
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000
>>> for x in range(1,11):
...     print '%2d %3d %4d' % (x, x*x, x*x*x)
... 
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

Observa que se ha añadido un espacio entre columnas por el modo en que funciona print: siempre añade un espacio entre sus argumentos.

Este ejemplo utiliza la función string.rjust(), que ajusta a la derecha una cadena dada una anchura determinada, añadiendo espacios a la izquierda. Existen las funciones relacionadas string.ljust() y string.center(). Estas funciones no escriben nada, sólo devuelven una cadena nueva. Si la cadena de entrada es demasiado larga, no la recortan, sino que la devuelven sin cambios. Se embrollará la salida, pero suele ser mejor que falsear los valores (si es preferible truncar la salida, siempre se puede agregar una operación de corte, como "string.ljust(x,n)[0:n]").

Existe otra función, string.zfill(), que rellena una cadena numérica por la izquierda con ceros. Entiende de signos positivo y negativo:

>>> import string
>>> string.zfill('12', 5)
'00012'
>>> string.zfill('-3.14', 7)
'-003.14'
>>> string.zfill('3.14159265359', 5)
'3.14159265359'
Usar el operador % tiene este aspecto:

>>> import math
>>> print 'El valor de PI es aproximadamente %5.3f.' % math.pi
El valor de PI es aproximadamente 3.142.

Si hay más de un formato en la cadena, se ha de pasar una tupla como operando derecho, como aquí:

>>> tabla = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for nombre, telef in tabla.items():
...     print '%-10s ==> %10d' % (nombre, telef)
... 
Jack       ==>       4098
Dcab       ==>       7678
Sjoerd     ==>       4127

La mayoría de los formatos funcionan como en C y exigen que se pase el tipo correcto. Sin embargo, de no hacerlo así, sólo se causa una excepción y no un volcado de memoria (o error de protección general). El formato %s es más relajado: Si el argumento correspondiente no es un objeto de cadena, se convierte a cadena mediante la función interna str(). Es posible pasar * para indicar la precisión o anchura como argumento (entero) aparte. No se puede utilizar los formatos de C %n ni %p.

Si tienes una cadena de formato realmente larga que no deseas dividir, sería un detalle hacer referencia a las variables por su nombre, en vez de por su posición. Esto se logra con una extensión a los formatos de C, con el formato %(nombre)formato, por ejemplo:

>>> tabla = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % tabla
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Esto resulta particularmente práctico en combinación con la nueva función interna vars(), que devuelve un diccionario que contiene todas las variables locales.


7.2 Lectura y escritura de ficheros

open() (abrir) devuelve un objeto fichero. Se suele usar con dos argumentos: "open(nombreFichero, modo)".

>>> f=open('/tmp/fichTrabajo', 'w')
>>> print f
<open file '/tmp/fichTrabajo', mode 'w' at 80a0960>

El primer argumento es una cadena que contiene el nombre del fichero. El segundo argumento es otra cadena que contiene caracteres que describen el modo en que se va a utilizar el fichero. El modo puede ser 'r', cuando sólo se va a leer del fichero, 'w', si sólo se va a escribir (y si existe un fichero del mismo nombre se borra) o 'a', que abre el fichero para añadir datos. En el modo 'a', cualquier dato que se escriba en el fichero se añadirá al final de los datos existentes. El argumento de modo es opcional. Se supone 'r' si se omite.

En Windows y Macintosh, al añadir 'b' al modo, el fichero se abre en modo binario, por lo que existen modos como 'rb', 'wb' y 'r+b'. Windows distingue entre ficheros de texto y binarios: los caracteres de fin de línea de los ficheros de texto se alteran ligeramente de forma automática al leer o escribir datos. Esta modificación oculta no afecta en el caso de ficheros de texto ASCII, pero corrompe los ficheros de datos binarios, tales como ficheros JPEG o .EXE. Ten mucho cuidado de utilizar el modo binario al leer y escribir dichos ficheros (observa que el comportamiento preciso del modo de texto en Macintosh depende de la biblioteca C subyacente).


7.2.1 Métodos de los objetos fichero

El resto de los ejemplos de esta sección supondrán que se ha creado previamente un objeto fichero denominado f.

Para leer el contenido de un fichero, llama a f.read(cantidad), que lee cierta cantidad de datos y los devuelve como cadena. cantidad es un argumento numérico opcional. Si se omite o es negativo, se leerá y devolverá el contenido completo del fichero. Es problema tuyo si el fichero tiene un tamaño descomunal. Si se incluye el argumento y es positivo, se leen y devuelven como máximo cantidad bytes. Si se había alcanzado el final de fichero, f.read() sólo devuelve una cadena vacía ("").

>>> f.read()
'Esto es el fichero completo.\012'
>>> f.read()
''

f.readline() lee una sola línea del fichero. Se deja un carácter de cambio de línea (\n) al final de la cadena, que se omite sólo en la última línea, siempre que el fichero no termine en un salto de línea. De este modo se consigue que el valor devuelto no sea ambiguo. Si f.readline() devuelve una cadena vacía, se ha alcanzado el final del fichero, mientras que una línea en blanco queda representada por '\n', una cadena que sólo contiene un salto de línea.

>>> f.readline()
'La primera línea del fichero.\012'
>>> f.readline()
'La segunda línea del fichero\012'
>>> f.readline()
''

f.readlines() devuelve una lista que contiene todas las líneas de datos del fichero. Si se llama con un parámetro opcional sizehint (estimación de tamaño), lee los bytes indicados del fichero, sigue hasta completar una línea y devuelve la lista de líneas. Se suele utilizar esto para leer un fichero grande de una manera eficaz por líneas, pero sin tener que cargar en memoria el fichero entero. Todas las líneas devueltas están completas

>>> f.readlines()
['La primera línea del fichero.\012', 'La segunda línea del fichero\012']

f.write(cadena) escribe el contenido de cadena al fichero y devuelve None.

>>> f.write('Probando, probando\n')

f.tell() devuelve un entero que indica la posición actual del objeto fichero dentro del fichero, medida en bytes desde el inicio del fichero. Para cambiar la posición del objeto fichero se usa "f.seek(desplazamiento, desde_dónde)". La posición se calcula sumando desplazamiento a un punto de referencia, seleccionado por el argumento desde_dónde. Un desde_dónde cero mide desde el inicio del fichero, 1 utiliza la posición actual y 2 utiliza el final del fichero como punto de referencia. desde_dónde se puede omitir, en cuyo caso se utiliza el valor cero y se mide desde el principio del fichero.

>>> f=open('/tmp/fichTrabajo', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5)     # Ir al 5º byte del fichero
>>> f.read(1)        
'5'
>>> f.seek(-3, 2) # Ir al 3er byte antes del final
>>> f.read(1)
'd'

Cuando termines de usar un fichero, llama a f.close() para cerrarlo y liberar los recursos del sistema que utilice el fichero. Tras llamar a f.close(), fracasará cualquier intento de usar el objeto fichero.

>>> f.close()
>>> f.read()
Traceback (innermost last):
  File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file

Los ficheros objeto tienen más métodos, como isatty() y truncate(), de uso menos frecuente. Consulta la Referencia de las bibliotecas si deseas ver una guía completa de los objetos fichero.


7.2.2 El módulo pickle

Es fácil escribir y leer cadenas de un fichero. Los números exigen un esfuerzo algo mayor, ya que el método read() sólo devuelve cadenas, que tendrán que pasarse a una función como string.atoi(), que toma una cadena como '123' y devuelve su valor numérico 123. Sin embargo, cuando se quiere guardar tipos de datos más complejos, como listas, diccionarios o instancias de clases, las cosas se complican bastante.

Mejor que hacer que los usuarios estén constantemente escribiendo y depurando código para guardar tipos de datos complejos, Python proporciona un módulo estándar llamado pickle7.1. Es un módulo asombroso que toma casi cualquier objeto de Python (¡hasta algunas formas de código Python!) y lo convierte a una representación de cadena. Este proceso se llama estibado. Reconstruir el objeto a partir de la representación en forma de cadena se llama desestibado. Entre el estibado y el desestibado, la cadena que representa el objeto puede ser almacenada en un fichero, en memoria o transmitirse por una conexión de red a una máquina remota.

Si partes de un objeto x y un objeto fichero f previamente abierto para escritura, el modo más sencillo de estibar el objeto sólo tiene una línea de código:

pickle.dump(x, f)

Para realizar el proceso inverso, si f es un objeto fichero abierto para escritura:

x = pickle.load(f)

Existen otras variaciones de este tema, que se utilizan para estibar muchos objetos o si no se quiere escribir los datos estibados a un fichero. Se puede consultar la documentación completa de pickle en la Referencia de las bibliotecas.

pickle es el método estándar para hacer que los objetos Python se puedan almacenar y reutilizar en otros programas o futuras ejecuciones del mismo programa. El término técnico que identifica esto es objeto persistente. Como pickle se utiliza tanto, muchos autores que escriben extensiones a Python tienen cuidado de asegurarse de que los nuevos tipos de datos, tales como matrices, se estiben y desestiben de la manera adecuada.



Notas al pie

...pickle7.1
N. del T. Pickle significa conservar en formol o encurtir, pero lo voy a traducir como estibar. Estibar es colocar los bultos en una nave para prepararla para el viaje, lo que se adapta bastante bien a la función de pickle.

Ver Sobre este documento... para obtener información sobre sugerencias.