Kitabı oku: «El gran libro de Python», sayfa 7

Yazı tipi:

El contexto de iteración

La instrucción for permite iterar de forma automática sobre los objetos iterables:


De hecho, utiliza el protocolo de iteración para completar automáticamente el proceso que hemos ejecutado manualmente en la sección anterior:

1. obtiene el iterador: iterador = iter(('a', 'b', 'c'));

2. solicita el elemento siguiente llamando al método iterador._ _next_ _();

3. si iterador._ _next_ _() genera una excepción StopIteration, entonces captura la excepción y termina la iteración; si no, pasa al punto 4;

4. asigna a la etiqueta i el elemento siguiente: i = iterador._ _next_ _();

5. ejecuta el bloque de instrucciones, que en este caso son la llamada simple a print(i);

6. regresa al punto 2.

También los diccionarios y los archivos son objetos iterables. Cuando se itera sobre un diccionario, se itera sobre sus claves: :


mientras que cuando se itera sobre un archivo, se itera sobre sus líneas:


Una importante carácterística de los objetos iterables es que pueden ser desempaquetados:


Ejercicio final

En esta sección haremos un ejercicio que nos servirá de resumen, y nos permitirá tanto repasar todo lo que hemos visto hasta ahora como introducir nuevos conceptos. Con este objetivo, vamos a analizar un script que lee archivos con extensión .data, realiza algunos cálculos y, por último, guarda los resultados en otro archivo. El script en cuestión es el archivo dataout.py:



NOTA

El código fuente de todos los ejercicios finales y de los ejemplos más significativos del libro está disponible en la URL http://code.google.com/p/the-pythonic-way/.

Es probable que gran parte de este código nos resulte confuso, pero no nos preocupemos, puesto que lo anlizaremos línea a línea en las secciones siguientes. De momento, volvamos a leerlo y esforcémonos al máximo por entender su significado nosotros solos.

Ejecución del script

El script lee del directorio actual los archivos con extensión .data y para cada uno de ellos crea un archivo de salida con el mismo nombre, pero con extensión .dataout. Por ejemplo, encuentra un archivo de entrada denominado 20121218.data, lo abre, lo elabora y guarda los resultados en un archivo de salida denominado 20121218.dataout. El script escribe sobre cada línea de los archivos de salida tres valores: el mínimo, el máximo y el medio de las correspondientes líneas de los archivos de entrada. Por ejemplo, si el script encuentra en el directorio actual el siguiente archivo de entrada:


lo abre, calcula los valores mínimo, máximo y medio de los elementos de la línea y los escribe en el archivo de salida:


Si el archivo de entrada tiene más de un línea, como el siguiente:


el archivo de salida tendrá el mismo número de líneas, cada una de las cuales con el valor mínimo, máximo y medio de la correspondiente línea del archivo de entrada:


El script acepta el argumento opcional desde la línea de comandos, que representa el nombre del directorio en el cual guardará el archivo. Si no se le pasa este argumento, los dos archivos de salida se guardarán en un directorio llamado out:


En cambio, si pasamos desde la línea de comandos un argumento, este se utilizará como en el directorio de salida:


Si el directorio de salida existe, el script lo señala con un primer mensaje en pantalla y después continúa su ejecución:



Ahora que ya sabemos qué hace el script, trataremos de entender el significado de su código. El primer paso consiste en descubrir cómo lee los argumentos pasados desde la línea de comandos.

Paso de los argumentos desde la línea de comandos

Los argumentos pasados al programa desde la línea de comandos son memorizados por Python en una lista accesible mediante el módulo sys. Esta lista se denomina sys.argv y contiene, como primer elemento, el nombre del archivo y como restantes, los otros argumentos pasados desde la línea de comandos:


NOTA

El nombre argv significa argument vector y procede de C, donde se utiliza habitualmente para indicar el parámetro al cual deben asignarse los argumentos pasados desde la línea de comandos:


Los elementos de sys.argv son cadenas, por lo que es preciso convertirlos en el tipo correcto para poder utilizarlos de manera apropiada:


En nuestro script el nombre del directorio de salida se asigna con la instrucción:


La expresión condicional a la derecha del signo igual valora como expresión de prueba la fragmentación de sys.argv, precisamente sys.argv[1:]. Como ya hemos dicho, ante una lista mylist, la fragmentación mylist[i:j] devuelve una lista de los elementos de mylist a partir de aquel con el índice i y hasta aquel con el índice j exclusive. Si el índice j se omite, la fragmentación devuelve todos los elementos a partir de aquel con el índice i, hasta el final de la lista. Consideremos, por ejemplo, el siguiente script:


y lo ejecutamos dos veces. La primera vez no le pasamos ningún argumento desde la línea de comandos, mientras que la segunda le pasamos tres argumentos:


Volviendo a nuestro código, cuando pasamos a python solo el nombre del archivo, la lista sys.argv[1:] queda vacía. Puesto que una lista vacía se valora como False en una prueba de verdad:


la expresión condicional sys.argv[1] ifsys.argv[1:] else 'out' devuelve 'out' y, por tanto, se asigna out_dir_name = 'out'.

En cambio, si además del nombre del archivo se pasa un argumento posterior, entonces sys.argv[1:] no queda vacía y se valora como True. En este caso, por tanto, se asigna out_dir_name = sys.argv[1].

Si quisiéramos realizar el análisis de los argumentos pasados desde la línea de comandos, de manera que aparezcan una ayuda y un mensaje de uso, o bien que se gestionen los errores si se pasan al programa argumentos no válidos, en tal caso será necesario utilizar el módulo argparse de la librería estándar. Dicho módulo realiza el análisis de los elementos de sys.argv, genera automáticamente los mensajes de uso y también gestiona los errores. Veremos un ejemplo de uso de este módulo en el Capítulo 2.

La interacción con el sistema operativo

Nuestro script debe ser capaz de interactuar con el sistema operativo, tanto para crear el directorio de salida, en el caso en que no exista, como para buscar los archivos con extensión .data dentro del directorio de trabajo. Como ya hemos mencionado anteriormente, es el módulo os quien proporciona esta interacción. De hecho, en el script hemos utilizado la función os.mkdir() para crear los directorios de salida:


Si el directorio existe, os.mkdir() genera una excepción de tipo FileExistsError:


Por esta razón, la instrucción que contiene os.mkdir() ha sido insertada dentro de la instrucción try. Así, si el directorio ya existe, se detecta una excepción y el flujo de ejecución pasa directamente a la cláusula except, saltando la print() inmediatamente después de os.mkdir(). A este punto, la suite de la except muestra un mensaje avisando de que el directorio ya existe, y la ejecución pasa a la instrucción for.

La función os.listdir() llamada sin argumentos devuelve una lista de los nombres de archivo y directorios incluidos dentro del directorio actual:


El bucle for itera sobre la lista devuelta por os.listdir() y ejecuta acciones solo si el nombre del archivo acaba en .data. Los archivos con la extensión .data son los siguientes:


Para cada uno de ellos debe crearse un archivo de salida cuyo nombre es otorgado por:


La función os.path.join() une una o más rutas, usando como separador el propio del sistema operativo en uso, por tanto, una barra inclinada en los sistemas Unix-like:


y una barra invertida en los sistemas Windows:


En nuestro caso, la ruta final es el resultado de la unión del nombre del directorio de salida con el nombre del archivo de salida. Por ejemplo, si el nombre del directorio de salida es out_dir_name = 'outdir' y el del archivo de entrada es file_name = '20110315.data', obtenemos:


Si, en lugar de utilizar os.path.join(), hubiéramos unido las rutas insertando el separador específico de nuestro sistema operativo, el script no habría sido portable, es decir, no habríamos podido ejecutarlo del mismo modo en todos los sistemas operativo. Para entendernos, si hubiéramos creado la ruta de este modo:


utilizando el separador específico de los sistemas Windows, nuestro programa no habría funcionado correctamente en los sistemas Unix-like. Consideremos, por ejemplo, el siguiente script:


Cuando lo ejecutamos en Windows, este crea un directorio de nombre out, para crear a continuación en su interior el archivo myfile.dataout. Sin embargo, la ruta pasada a open() utiliza el separador específico de Windows, por lo que el script no es portable. De hecho, en un sistema Unix-like no se crea un archivo myfile.dataout dentro del directorio out, sino un archivo out\myfile.dataout en el directorio actual:


Es muy importante conocer el módulo os, puesto que proporciona múltiples herramientas que nos permiten escribir programas portables.

NOTA

El objeto al que hace referencia os.path es un módulo, que es distinto según el sistema operativo en uso. Por ejemplo, en los sistemas Unix-like os.path se refiere al módulo posixpath:


mientras que en los sistemas Windows se refiere al módulo ntpath:


Por tanto, si llamamos a la función os.path.join() desde un sistema Unix- like, en realidad la función que estamos llamando es posixpath.path.join(), mientras que si llamamos a os.path.join() desde un sistema Windows, estamos llamando realmente a la función ntpath.path.join().

Acabamos esta introducción al módulo os con un consejo: leed la documentación online del sitio oficial, que encontraréis en la página http://docs.python.org/3/library/ os.html.

Listas por comprensión

El bucle for más interno itera en las líneas del archivo. Para cada línea se crea una lista de los elementos convertidos en float:


La expresión [float(item) for item in line.split()] es una lista por comprensión. Si ha cemos memoria, recordaremos que ya hemos hablado de ella en la sección La función integrada help() y las cadenas de documentación, en la cual hemos dicho que es una expresión que permite crear una lista con una sintaxis elegante y compacta. Retomemos de nuevo el argumento, dejando para el Capítulo 2 un trato más detallado.

Vamos a intentar entender qué hace exactamente nuestra lista por comprensión, partiendo del método line.split():


Como podemos ver, line.split() devuelve una lista de los elementos de la cadena de texto. Sin embargo, esta lista no puede ser utilizada para calcular una suma, porque sus elementos son cadenas y no números:


Por tanto, es preciso obtener una lista cuyos elementos sean float y no str. Una solución podría ser la siguiente:


En cambio, la solución que hemos adoptado en nuestro script utiliza una lista por comprensión y, como podemos ver, es mucho más compacta y elegante que la anterior:


Su sintaxis es autoexplicativa: cada elemento item de line.split() se convierte en float y se inserta en la lista.

Expresiones de formato de las cadenas de texto

Todavía nos falta una pieza para completar el rompecabezas:


NOTA

A partir de Python 3.4, la mejor manera de calcular la media es utilizando la función mean() del módulo statistics. Para más información, consultad la documentación oficial, en la página http://docs.python.org/3/library/statistics.html.

El símbolo % en el interior de una cadena se muestra de por sí como el resto de los caracteres:


Sin embargo, si la cadena es seguida por un símbolo % que, a su vez, es seguido por un literal o una etiqueta, el %f en el interior de la cadena tiene un significado especial:


Como podemos ver, el %f ha sido sustituido respectivamente por el literal y por la etiqueta que siguen al símbolo % fuera de la cadena. El carácter f indica que el literal o la etiqueta se convertirán en float:


También es posible especificar el número de las cifras decimales a visualizar. Para ello, se ubica el .numero_cifre delante de la f, como se muestra a continuación:


Si queremos dar formato a más objetos a la vez, debemos agruparlos en una tupla detrás del símbolo % fuera de la cadena, como se muestra a continuación:


Además de la aplicación de formato con conversión en float mediante %f, también es posible utilizar otras codificaciones. Por ejemplo, %d convierte en entero, %s, en cadena, entre otros. Veremos todos los posibles modos de formato de las cadenas en el Capítulo 2.

Si releemos el script dataout.py, nos daremos cuenta de que todo aquello que inicialmente nos parecía incomprensible ahora ya tiene sentido.

Ahora que tenemos una visión de conjunto del lenguaje, podemos afrontar con soltura los próximos capítulos.

2

El corazón del lenguaje

Este capítulo se centra principalmente en los tipos de datos básicos y sus instancias: números, cadenas, listas, tuplas, diccionarios y conjuntos. Nos detendremos para describir con todo detalle los mecanismos de codificación de las cadenas y el vínculo entre bytes y caracteres. Hablaremos de nuevo de objetos iterables y, al final del capítulo, veremos cómo efectuar el análisis de los argumentos pasados desde la línea de comandos, cómo gestionar la fecha y la hora, y cómo trabajar con las colecciones.

Números

La familia de los tipos integrados numéricos es la siguiente:

• el tipo int, cuyas instancias representan números enteros;

• el tipo float, cuyas instancias representan los números de punto flotante;

• el tipo complex, cuyas instancias representan los números complejos;

• el tipo bool, que tiene dos únicas instancias, a las cuales se refiere mediante las palabras clave True y False.

Las instancias de los tipos numéricos, que llamaremos números, son objetos inmutables y resultan instancias también del tipo numbers.Number:


El mundo de los números en Python no está vinculado a los únicos tipos numéricos integrados y a sus instancias, sino que, como veremos, implica algunos módulos de la biblioteca estándar y varias funciones incorporadas.

Números enteros

Los números enteros en Python no están limitados inferiormente ni superiormente, y cada entero tiene cuatro representaciones literales:

1. la representación en base 10, denominada decimal: es la predeterminada, y los enteros se representan mediante cifras decimales únicas, como los números 12 (doce) y 99 (noventa y nueve);

2. la representación en base 2, denominada binaria: los enteros se representan mediante su codificación en binario, precedida por 0b; por ejemplo, la representación binaria del número 7 decimal es 0b111;

3. la representación en base 8, denominada octal: los enteros se representan mediante su codificación octal, precedida por 0o; por ejemplo, la representación octal del número 10 decimal es 0o12;

4. la representación en base 16, denominada hexadecimal: los enteros se representan mediante su codificación hexadecimal, precedida por 0x: por ejemplo, el número 10 decimal es proporcionado por 0xA.

Así, el número entero -15 puede ser representado de las siguientes formas:


Las letras dentro de los literales pueden mostrarse indistintamente en mayúsculas o en minúsculas:


NOTA

Si no queda claro el significado de las distintas representaciones numéricas, podéis consultar la página https://es.wikipedia.org/wiki/Sistema_de_numeración. En los artículos relacionados se muestran referencias a los distintos sistemas de numeración, incluidos el decimal, binario, octal y hexadecimal.

Ante un número con representación decimal, las funciones integradas bin(), oct() y hex() permiten obtener, respectivamente, su representación binaria, octal y hexadecimal en forma de cadena:


Para convertir el contenido de una cadena en un número entero se puede utilizar la clase int:


Esta puede ser llamada pasándole un segundo argumento opcional, utilizado para indicar la base con la cual el número está representado:


Por defecto, int considera los números en representación decimal, por lo que si se le pasa una representación distinta sin especificar la base, se genera una excepción:


Veamos algun otro ejemplo:


También la función integrada eval() convierte una cadena en un número, aunque su comportamiento es más general, puesto que evalúa y ejecuta expresiones contenidas en una cadena, devolviendo el resultado:


Operadores que permiten procesar los números enteros

En esta sección, veremos las operaciones más importantes que podemos realizar con los números enteros.

Elevación a la potencia

Como ya sabemos, el símbolo ** representa el operador de elevación a la potencia:


Los números enteros no tienen límite superior ni inferior, por lo que la siguiente elevación a la potencia no genera ningún error, sino que se calcula correctamente:


NOTA

En Python 2 los números enteros no son representados solo por el tipo int, sino también por el tipo long. Si el valor del entero está comprendido entre -sys.maxint - 1 y sys.maxint, se representa con el tipo int; si no, con el tipo long:


El valor de sys.maxint depende del número de bytes utilizados para representar las variables de tipo int en lenguaje C, por lo que está vinculado a la arquitectura de nuestra máquina y al sistema operativo en uso. Por ejemplo, en una máquina con arquitectura de 64 bits y un sistema operativo Unix-like, sys.maxint es igual a 9223372036854775807, mientras que en un sistema Windows su arquitectura de 64 bits es igual a 2147483647.

División natural y floor

El operador / realiza una división entre enteros y genera un float (consulte PEP-0238):


Siempre se genera un float, incluso cuando el resultado podría ser un int:


El operador //, independientemente del tipo de sus operandos, redondea el resultado de la división al entero inferior. Este tipo de división se denomina división floor, y genera un int en el caso en que ambos operandos sean enteros:


NOTA

El significado del operador / es uno de los puntos de rotura más importantes entre Python 2 y Python 3. En Python 2, de hecho, este operador realiza una división floor:


En Python 2 también podemos obtener una división natural:


₺548,57

Türler ve etiketler

Yaş sınırı:
0+
Hacim:
1892 s. 2471 illüstrasyon
ISBN:
9788426729057
Yayıncı:
Telif hakkı:
Bookwire
İndirme biçimi:
Metin
Ortalama puan 0, 0 oylamaya göre
Ses
Ortalama puan 0, 0 oylamaya göre
Metin
Ortalama puan 0, 0 oylamaya göre
Metin
Ortalama puan 4,8, 5 oylamaya göre
Metin
Ortalama puan 0, 0 oylamaya göre
Metin
Ortalama puan 0, 0 oylamaya göre
Metin
Ortalama puan 0, 0 oylamaya göre