-
Notifications
You must be signed in to change notification settings - Fork 3
/
cap3.tex
487 lines (347 loc) · 25.3 KB
/
cap3.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
% ch3.tex
% This work is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 New Zealand License.
% To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/nz
% or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
\chapter{Comprensiones}\label{ch:comprensiones}
\noindent
Nivel de dificultad:\difll
\begin{citaCap}
``Nuestra imaginación está desplegada a más no poder,
no como en la ficción, para imaginar las cosas que no están realmente ahí,\\
sino para entender aquellas que sí lo están.''\\
---\href{http://en.wikiquote.org/wiki/Richard\_Feynman}{Rychard Feynman}
\end{citaCap}
\section{Inmersión}
Este capítulo te explicará las listas por comprensión, diccionarios por comprensión y conjuntos por comprensión: tres conceptos centrados alrededor de una técnica muy potente. Pero antes vamos a dar un pequeño paseo alrededor de dos módulos que te van a servir para navegar por tu sistema de ficheros.
\section{Trabajar con ficheros y directorios}
Python 3 posee un módulo denominado \codigo{os} que es la contracción de ``operating system''\footnote{Sistema Operativo.}. El módulo \href{http://docs.python.org/3.1/library/os.html}{\codigo{os}} contiene un gran número de funciones para recuperar ---y en algunos casos, modificar--- información sobre directorios, ficheros, procesos y variables del entorno local. Python hace un gran esfuerzo por ofrecer una \codigo{API} unificada en todos los sistemas operativos que soporta, por lo que tus programas pueden funcionar en casi cualquier ordenador con el mínimo de código específico posible.
\subsection{El directorio de trabajo actual}
Cuando te inicias en Python, pasas mucho tiempo en la consola interactiva. A lo largo del libro verás muchos ejemplos que siguen el siguiente patrón:
\begin{enumerate}
\item Se importa uno de los módulos de la carpeta de \codigo{ejemplos}.
\item Se llama a una función del módulo.
\item Se explica el resultado.
\end{enumerate}
Si no sabes cuál es el directorio actual de trabajo, el primer paso probablemente elevará la excepción \codigo{ImportError}. ¿Por qué? Porque Python buscará el módulo en el camino de búsqueda actual (ver capítulo~\ref{sc:search_path}), pero no lo encontrará porque la carpeta \codigo{ejemplos} no está incluida en él. Para superar este problema hay dos soluciones posibles:
\begin{enumerate}
\item Añadir el directorio de \codigo{ejemplos} al camino de búsqueda de importación.
\item Cambiar el directorio de trabajo actual a la carpeta de \codigo{ejemplos}.
\end{enumerate}
El directorio de trabajo actual es una propiedad invisible que Python mantiene en memoria. Siempre existe un directorio de trabajo actual: en la consola interactiva de Python; durante la ejecución de un programa desde la línea de comando, o durante la ejecución de un programa Python como un \codigo{CGI} de algún servidor web.
El módulo \codigo{os} contiene dos funciones que te permiten gestionar el directorio de trabajo actual.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os
>>> print(os.getcwd())
/home/jmgaguilera
>>> os.chdir('/home/jmgaguilera/inmersionenpython3/ejemplos')
>>> print(os.getcwd())
/home/jmgaguilera/inmersionenpython3/ejemplos
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 1:} El módulo \codigo{os} viene instalado con Python. Puedes importarlo siempre que lo necesites.
\item \emph{Línea 2:} Utiliza la función \codigo{os.getcwd()} para recuperar el directorio de trabajo actual. Cuando ejecutas la consola interactiva, Python toma como directorio de trabajo actual aquél en el que te encontrases en el sistema operativo antes de entrar en la consola; si ejecutas la consola desde una opción de menú del sistema operativo, el directorio de trabajo será aquél en el que se encuentre el programa ejecutable de Python o tu directorio de trabajo por defecto\footnote{Esto depende del sistema operativo: windows, linux, ...}.
\item \emph{Línea 4:} Utiliza la función \codigo{os.chdir()} para cambiar de directorio. Conviene utilizar la convención de escribir los separadores en el estilo de Linux (con las barras inclinadas adelantadas) puesto que este sistema es universal y funciona también en Windows. Este es uno de los lugares en los que Python intenta ocultar las diferencias entre sistemas operativos.
\end{enumerate}
\subsection{Trabajar con nombres de ficheros y directorios}
Aprovechando que estamos viendo los directorios, quiero presentarte el módulo \codigo{os.path}, que contiene funciones para manipular nombres de ficheros y directorios.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os
>>> print(os.path.join('/home/jmgaguilera/inmersionenpython3/ejemplos/',
'parahumanos.py'))
/home/jmgaguilera/inmersionenpython3/ejemplos/parahumanos.py
>>> print(os.path.join('/home/jmgaguilera/inmersionenpython3/ejemplos',
'parahumanos.py'))
/home/jmgaguilera/inmersionenpython3/ejemplos/parahumanos.py
>>> print(os.path.expanduser('~'))
/home/jmgaguilera
>>> print(os.path.join(os.path.expanduser('~'),
'inmersionenpython3', 'examples',
'humansize.py'))
/home/jmgaguilera/inmersionenpython3/ejemplos\parahumanos.py
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} La función \codigo{os.path.join()} construye un nombre completo de fichero o directorio (nombre de path) a partir de uno o más partes. En este caso únicamente tiene que concatenar las cadenas.
\item \emph{Línea 5:} Este caso es menos trivial. La función añade una barra inclinada antes de concatenar. Dependiendo de que el ejemplo se construya en Windows o en una versión de Linux o Unix, la barra inclinada será invertida o no. Python será capaz de encontrar el fichero o directorio independientemente del sentido en el que aparezcan las barras inclinadas. En este caso, como el ejemplo lo construí en Linux, la barra inclinada es la típica de Linux.
\item \emph{Línea 8:} La función \codigo{os.path.expanduser()} obtendrá un camino completo al directorio que se exprese y que incluye \codigo{~} como indicador el directorio raíz del usuario conectado. Esto funcionará en todos los sistemas operativos que tengan el concepto de ``directorio raíz del usuario'', lo que incluye OS X, Linux, Unix y Windows. El camino que se retorna no lleva la barra inclinada al final, pero, como hemos visto, a la función \codigo{os.path.join()} no le afecta.
\item \emph{Línea 10:} Si combinamos estas técnicas podemos construir fácilmente caminos completos desde el directorio raíz del usuario. La función \codigo{os.path.join()} puede recibir cualquier número de parámetros. Yo me alegré mucho al descubrir esto puesto que la función \codigo{anyadirBarra()} es una de las típicas que siempre tengo que escribir cuando aprendo un lenguaje de programación nuevo. \emph{No escribas} esta estúpida función en Python, personas inteligentes se ha ocupado de ello por ti.
\end{enumerate}
El módulo \codigo{os.path} también contiene funciones para trocear caminos completos, nombres de directorios y nombres de fichero en sus partes constituyentes.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> nombrepath = '/home/jmgaguilera/inmersionenpython3/parahumanos.py'
>>> os.path.split(nombrepath)
('/home/jmgaguilera/inmersionenpython3/ejemplos', 'parahumanos.py')
>>> (nombredir, nombrefich) = os.path.split(nombrepath)
>>> nombredir
'/home/jmgaguilera/inmersionenpython3/ejemplos'
>>> nombrefich
'parahumanos.py'
>>> (nombrecorto, extension) = os.path.splitext(nombrefich)
>>> nombrecorto
'parahumanos'
>>> extension
'.py'
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} La función \codigo{split()} divide un camino completo en dos partes que contienen el camino y el nombre de fichero. Los retorna en una tupla.
\item \emph{Línea 4:} ¿Recuerdas cuando dije que podías utilizar la asignación múltiple para devolver varios valores desde una función? \codigo{os.path.split()} hace exactamente eso. Puedes asignar los valores de la tupla que retorna a dos variables. Cada variable recibe el valor que le corresponde al elemento de la tupla.
\item \emph{Línea 5:} La primera variable, \codigo{nombredir}, recibe el valor del primer elemento de la tupla que retorna \codigo{os.path.split()}, el camino al fichero.
\item \emph{Línea 7:} La segunda variable, \codigo{nombrefich}, recibe el valor del segundo elemento de la tupla que retorna \codigo{os.path.split()}, el nombre del fichero.
\item \emph{Línea 9:} \codigo{os.path} también posee la función \codigo{os.path.splitext()} que divide el nombre de un fichero en una tupla que contiene el nombre y la extensión separados en dos elementos. Puedes utilizar la misma técnica que antes para asignarlos a dos variables separadas.
\end{enumerate}
\subsection{Listar directorios}
El módulo \codigo{glob} es otra herramienta incluida en la librería estándar de Python. Proporciona una forma sencilla de acceder al contenido de un directorio desde un programa. Utiliza los caracteres \emph{comodín} que suelen usarse en una consola de línea de comandos.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> os.chdir('/home/jmgaguilera/inmersionenpython3/')
>>> import glob
>>> glob.glob('ejemplos/*.xml')
['ejemplos\\feed-broken.xml',
'ejemplos\\feed-ns0.xml',
'ejemplos\\feed.xml']
>>> os.chdir('ejemplos/')
>>> glob.glob('*test*.py')
['alphameticstest.py',
'pluraltest1.py',
'pluraltest2.py',
'pluraltest3.py',
'pluraltest4.py',
'pluraltest5.py',
'pluraltest6.py',
'romantest1.py',
'romantest10.py',
'romantest2.py',
'romantest3.py',
'romantest4.py',
'romantest5.py',
'romantest6.py',
'romantest7.py',
'romantest8.py',
'romantest9.py']
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 3:} El módulo \codigo{glob} utiliza comodines y devuelve el camino de todos los ficheros y directorios que coinciden con la búsqueda. En este ejemplo se busca un directorio que contenga ficheros terminados en ``*.xml'', lo que encontrará todos los ficheros xml que se encuentren en el directorio de ejemplos.
\item \emph{Línea 7:} Ahora cambio el directorio de trabajo al subdirectorio \codigo{ejemplos}. La función \codigo{os.chdir()} puede recibir como parámetro un camino relativo a la posición actual.
\item \emph{Línea 8:} Puedes incluir varios comodines de búsqueda. El ejemplo encuentra todos los ficheros del directorio actual de trabajo que incluyan la palabra \codigo{test} en alguna parte del nombre y que, además, terminen con la cadena \codigo{.py}.
\end{enumerate}
\subsection{Obtener metadatos de ficheros}
Todo sistema de ficheros moderno almacena metadatos sobre cada fichero: fecha de creación, fecha de la última modificación, tamaño, etc. Python proporciona una \codigo{API} unificada para acceder a estos metadatos. No necesitas abrir el fichero, únicamente necesitas su nombre.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os
>>> print(os.getcwd())
/home/jmgaguilera/inmersionenpython3/ejemplos
>>> metadata = os.stat('feed.xml')
>>> metadata.st_mtime
1247520344.9537716
>>> import time
>>> time.localtime(metadata.st_mtime)
time.struct_time(tm_year=2009, tm_mon=7, tm_mday=13, tm_hour=17,
tm_min=25, tm_sec=44, tm_wday=0, tm_yday=194, tm_isdst=1)
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} El directorio de trabajo actual es \codigo{ejemplos}.
\item \emph{Línea 4:} \codigo{feed.xml} es un fichero que se encuentra en el directorio \codigo{ejemplos}. La función \codigo{os.stat()} devuelve un objeto que contiene diversos metadatos sobre el fichero.
\item \emph{Línea 5:} \codigo{st\_mtime} contiene la fecha y hora de modificación, pero en un formato que no es muy útil (Técnicamente es el número de segundos desde el inicio de la Época, que está definida como el primer segundo del 1 de enero de 1970 ¡En serio!).
\item \emph{Línea 7:} El módulo \codigo{time} forma parte de la librería estándar de Python. Contiene funciones para convertir entre diferentes representaciones del tiempo, formatear valores de tiempo en cadenas y manipular las referencias a los husos horarios.
\item \emph{Línea 8:} La función \codigo{time.localtime()} convierte un valor de segundos desde el inicio de la época (que procede la propiedad anterior) en una estructura más útil que contiene año, mes, día, hora, minuto, segundo, etc. Este fichero se modificó por última vez el 13 de julio de 2009 a las 5:25 de la tarde.
\end{enumerate}
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
# continuaci$\ac{o}$n del ejemplo anterior
>>> metadata.st_size
3070
>>> import parahumanos
>>> parahumanos.tamnyo_aproximado(metadata.st_size)
'3.0 KiB'
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} La función \codigo{os.stat()} también devuelve el tamaño de un fichero en la propiedad \codigo{st\_size}. El fichero \codigo{feed.xml} ocupa \codigo{3070} bytes.
\item \emph{Línea 5:} Aprovecho la función \codigo{tamanyo\_aproximado()} para verlo de forma más clara.
\end{enumerate}
\subsection{Construcción de caminos absolutos}
En el apartado anterior, se observó cómo la función \codigo{glob.glob()} devolvía una lista de nombres relativa. El primer ejemplo mostraba caminos como \codigo{``ejemplos/feed.xml''}, y el segundo ejemplo incluso tenía nombres más cortos como \codigo{``romantest1.py''}. Mientras permanezcas en el mismo directorio de trabajo los path relativos funcionarán sin problemas para recuperar información de los ficheros. No obstante, si quieres construir un camino absoluto ---Uno que contenga todos los directorios hasta el raíz del sistema de archivos--- lo que necesitas es la función \codigo{os.path.realpath()}.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os
>>> print(os.getcwd())
/home/jmgaguilera/inmersionenpython3/ejemplos
>>> print(os.path.realpath('feed.xml'))
/home/jmgaguilera/inmersionenpython3/ejemplos/feed.xml
\end{lstlisting}
\end{minipage}
\section{Listas por comprensión}
La creación de listas por comprensión proporciona una forma compacta de crear una lista a partir de otra mediante la realización de una operación a cada uno de los elementos de la lista original.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> una_lista = [1, 9, 8, 4]
>>> [elem * 2 for elem in una_lista]
[2, 18, 16, 8]
>>> una_lista
[1, 9, 8, 4]
>>> una_lista = [elem * 2 for elem in una_lista]
>>> una_lista
[2, 18, 16, 8]
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} Para explicar esto es mejor leerlo de derecha a izquierda. \codigo{una\_lista} es la lista origen que se va a recorrer para generar la nueva lista. El intérprete de Python recorre cada uno de los elementos de \codigo{una\_lista}, asignando temporalmente el valor de cada elemento a la variable \codigo{elem}. Después Python aplica la operación que se haya indicado, en este caso \codigo{elem * 2}, y el resultado lo añade a la nueva lista.
\item \emph{Línea 4:} Como se observa, la lista original no cambia.
\item \emph{Línea 6:} No pasa nada por asignar el resultado a la variable que tenía la lista original. Python primero construye la nueva lista en memoria y luego asigna el resultado a la variable.
\end{enumerate}
Para crear una lista de esta forma, puedes utilizar cualquier expresión válida de Python, como por ejemplo las funciones del módulo \codigo{os} para manipular ficheros y directorios.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os, glob
>>> glob.glob('*.xml')
['feed-broken.xml', 'feed-ns0.xml', 'feed.xml']
>>> [os.path.realpath(f) for f in glob.glob('*.xml')]
['/home/jmgaguilera/inmersionenpython3/ejemplos/feed-broken.xml',
'/home/jmgaguilera/inmersionenpython3/ejemplos/feed-ns0.xml',
'/home/jmgaguilera/inmersionenpython3/ejemplos/feed.xml']
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} Esta llamada retorna una lista con todos los ficheros terminados en \codigo{.xml} del directorio de trabajo.
\item \emph{Línea 4:} Esta lista generada por comprensión toma la lista original y la transforma en una nueva lista con los nombres completos de ruta.
\end{enumerate}
Las listas por comprensión también permiten filtrar elementos, generando una lista cuyo tamaño sea menor que el original.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os, glob
>>> [f for f in glob.glob('*.py') if os.stat(f).st_size > 6000]
['pluraltest6.py',
'romantest10.py',
'romantest6.py',
'romantest7.py',
'romantest8.py',
'romantest9.py']
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} Para filtrar una lista puedes incluir la cláusula \codigo{if} al final de la comprensión. Esta expresión se evalúa para cada elemento de la lista original. Si el resultado es verdadero, el elemento será calculado e incluido en el resultado. En este caso se seleccionan todos los ficheros que terminan en \codigo{.py} que se encuentren en el directorio de trabajo, se comprueba si son de tamaño mayor a \codigo{6000} bytes. Seis de ellos cumplen este requisito, por lo que son los que aparecen en el resultado final.
\end{enumerate}
Hasta el momento, todos los ejemplos de generación de listas por comprensión han utilizado expresiones muy sencillas ---multiplicar un número por una constante, llamada a una función o simplemente devolver el elemento original de la lista--- pero no existe límite en cuanto a la complejidad de la expresión.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os, glob
>>> [(os.stat(f).st_size, os.path.realpath(f)) for
... f in glob.glob('*.xml')]
[(3074,
'c:/home/jmgaguilera/inmersionenpython3/ejemplos/feed-broken.xml'),
(3386,
'c:/home/jmgaguilera/inmersionenpython3/ejemplos/feed-ns0.xml'),
(3070, 'c:/home/jmgaguilera/inmersionenpython3/ejemplos/feed.xml')]
>>> import parahumanos
>>> [(parahumanos.tamanyo_aproximado(os.stat(f).st_size), f)
for f in glob.glob('*.xml')]
[('3.0 KiB', 'feed-broken.xml'),
('3.3 KiB', 'feed-ns0.xml'),
('3.0 KiB', 'feed.xml')]
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} En este caso se buscan los ficheros que finalizan en \codigo{.xml} en el directorio de trabajo actual, se recupera su tamaño (mediante una llamada a la función \codigo{os.stat()}) y se construye una tupla con el tamaño del fichero y su ruta completa (mediante una llamada a \codigo{os.path.realpath()}.
\item \emph{Línea 10:} En este caso se aprovecha la lista anterior para generar una nueva con el tamaño aproximado de cada fichero.
\end{enumerate}
\section{Diccionarios por comprensión}
Es similar al apartado anterior pero genera un diccionario en lugar de una lista.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os, glob
>>> metadata = [(f, os.stat(f)) for f in glob.glob('*test*.py')]
>>> metadata[0]
('alphameticstest.py', nt.stat_result(st_mode=33206, st_ino=0,
st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=2509,
st_atime=1247520344, st_mtime=1247520344, st_ctime=1247520344))
>>> metadata_dict = {f:os.stat(f) for f in glob.glob('*test*.py')}
>>> type(metadata_dict)
<class 'dict'>
>>> list(metadata_dict.keys())
['romantest8.py', 'pluraltest1.py', 'pluraltest2.py', 'pluraltest5.py',
'pluraltest6.py', 'romantest7.py', 'romantest10.py', 'romantest4.py',
'romantest9.py', 'pluraltest3.py', 'romantest1.py', 'romantest2.py',
'romantest3.py', 'romantest5.py', 'romantest6.py',
'alphameticstest.py', 'pluraltest4.py']
>>> metadata_dict['alphameticstest.py'].st_size
2509
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 2:} Esto no genera un diccionario por comprensión, genera una lista por comprensión. Encuentra todos los ficheros terminados en \codigo{.py} con el texto \codigo{test} en el nombre y luego construye una tupla con el nombre y los metadatos del fichero (llamando a la función \codigo{os.stat()}).
\item \emph{Línea 3:} Cada elemento de la lista resultante es una tupla.
\item \emph{Línea 7:} Esto sí es una generación de un diccionario por comprensión. La sintaxis es similar a la de la generación de listas, con dos diferencias: primero, se encierra entre llaves en lugar de corchetes; segundo, en lugar de una única expresión para cada elemento, contiene dos expresiones separadas por dos puntos. La expresión que va delante de los dos puntos es la clave del diccionario y la expresión que va detrás es el valor (\codigo{os.stat(f)} en este ejemplo).
\item \emph{Línea 8:} El resultado es un diccionario.
\item \emph{Línea 10:} La claves de este caso particular son los nombres de los ficheros.
\item \emph{Línea 16:} El valor asociado a cada clave es el valor que retornó la función \codigo{os.stat()}. Esto significa que podemos utilizar este diccionario para buscar los metadatos de un fichero a partir de su nombre. Uno de los elementos de estos metadatos es \codigo{st\_size}, el tamaño de fichero. Para el fichero \codigo{alphameticstest.py} el valor es \codigo{2509} bytes.
\end{enumerate}
Como con las listas, puedes incluir la cláusula \codigo{if} para filtrar los elementos de entrada mediante una expresión que se evalúa para cada uno de los elementos.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> import os, glob, parahumanos
>>> dict = {os.path.splitext(f)[0]:parahumanos.tamanyo_aproximado(
os.stat(f).st_size) \
... for f in glob.glob('*') if os.stat(f).st_size > 6000}
>>> list(dict.keys())
['romantest9', 'romantest8', 'romantest7', 'romantest6',
'romantest10', 'pluraltest6']
>>> dict['romantest9']
'6.5 KiB'
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 4:} Este ejemplo construye una lista con todos los ficheros del directorio de trabajo actual (\codigo{glob.glob('*')}), filtra la lista para incluir únicamente aquellos ficheros mayores de \codigo{6000} bytes (\codigo{if os.stat(f).s\_size $>$ 6000}) y utiliza la lista filtrada para construir un diccionario cuyas claves son los nombres de fichero menos la extensión (\codigo{os.path.splitext(f)[0]}) y los valores el tamaño de cada uno de ellos (\codigo{parahumanos.tamanyo\_aproximado(os.stat(f).st\_size)}).
\item \emph{Línea 5:} Como viste en el ejemplo anterior son seis ficheros, por lo que hay seis elementos en el diccionario.
\item \emph{Línea 7:} El valor de cada elemento es la cadena que retorna la función \codigo{tamanyo\_aproximado()}.
\end{enumerate}
\subsection{Trucos que se pueden hacer}
Te presento un truco que puede serte de utilidad: intercambiar las claves y valores de un diccionario.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> dict = {'a': 1, 'b': 2, 'c': 3}
>>> {value:key for key, value in a_dict.items()}
{1: 'a', 2: 'b', 3: 'c'}
\end{lstlisting}
\end{minipage}
\section{Conjuntos por comprensión}
Por último mostraré la sintaxis para generar conjuntos por comprensión. Es muy similar a la de los diccionarios, con la única diferencia de que únicamente se incluyen valores en lugar de parejas clave-valor.
\noindent\begin{minipage}{\textwidth}
\begin{lstlisting}[mathescape=True]
>>> conjunto = set(range(10))
>>> conjunto
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> {x ** 2 for x in conjunto}
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}
>>> {x for x in conjunto if x % 2 == 0}
{0, 8, 2, 4, 6}
>>> {2**x for x in range(10)}
{32, 1, 2, 4, 8, 64, 128, 256, 16, 512}
\end{lstlisting}
\end{minipage}
\begin{enumerate}
\item \emph{Línea 4:} Los conjuntos generados por comprensión pueden partir de otro conjunto en lugar de una lista. En este ejemplo se calcula el cuadrado de cada uno de los elementos (los números del 0 al 9).
\item \emph{Línea 6:} Como en el caso de las listas y diccionarios, puedes incluir una cláusula \codigo{if} para filtrar elementos antes de calcularlos e incluirlos en el resultado.
\item \emph{Línea 8:} Los conjuntos por comprensión no necesitan tomar un conjunto como entrada, pueden partir de cualquier tipo de secuencia.
\end{enumerate}
\section{Lecturas complementarias}
\begin{itemize}
\item \href{http://docs.python.org/3.1/library/os.html}{módulo \codigo{os}}
\item \href{http://www.doughellmann.com/PyMOTW/os/}{\codigo{os} --- Portabilidad en el acceso a características específicas del sistema operativo}
\item \href{http://docs.python.org/3.1/library/os.path.html}{módulo \codigo{os.path}}
\item \href{http://www.doughellmann.com/PyMOTW/ospath/}{Manipulación de los nombres de fichero independiente de la plataforma --- \codigo{os.path}}
\item \href{http://docs.python.org/3.1/library/glob.html}{módulo \codigo{glob}}
\item \href{http://www.doughellmann.com/PyMOTW/glob/}{Patrones de búsqueda de ficheros --- \codigo{glob}}
\item \href{http://docs.python.org/3.1/library/time.html}{módulo \codigo{time}}
\item \href{http://www.doughellmann.com/PyMOTW/time/}{Funciones para manipulación de hora -- \codigo{time}}
\item \href{http://docs.python.org/3.1/tutorial/datastructures.html#list-comprehensions}{Listas por comprensión}
\item \href{http://docs.python.org/3.1/tutorial/datastructures.html#nested-list-comprehensions}{Comprensiones anidadas}
\item \href{http://docs.python.org/3.1/tutorial/datastructures.html#looping-techniques}{Técnicas para hacer bucles}
\end{itemize}