-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile.html
607 lines (486 loc) · 54.4 KB
/
file.html
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
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
<!DOCTYPE html>
<meta charset=utf-8>
<title>File - Immersione in Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 11}
mark{display:inline}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8> <input type=search name=q size=25 placeholder="powered by Google™"> <input type=submit name=sa value=Search></div></form>
<p>Voi siete qui: <a href=index.html>Inizio</a> <span class=u>‣</span> <a href=indice.html#file>Immersione in Python 3</a> <span class=u>‣</span>
<p id=level>Livello di difficoltà: <span class=u title=intermedio>♦♦♦♢♢</span>
<h1>File</h1>
<blockquote class=q>
<p><span class=u>❝</span> Camminare per nove miglia non è uno scherzo, specialmente se piove. <span class=u>❞</span><br>— Harry Kemelman, <cite>Nove miglia sotto la pioggia</cite>
</blockquote>
<p id=toc>
<h2 id=divingin>Immersione!</h2>
<p class=f>Occasionalmente, il mio portatile Windows aveva 38.493 file prima che installassi una sola applicazione. L’installazione di Python 3 ha aggiunto quasi 3.000 file a quel totale. I file sono il paradigma di memorizzazione principale di tutti i sistemi operativi più importanti; il concetto è talmente radicato che la maggior parte delle persone avrebbe difficoltà a <a href=http://en.wikipedia.org/wiki/Computer_file#History>immaginare un’alternativa</a>. Il vostro computer, metaforicamente parlando, annega nei file.
<h2 id=reading>Leggere dai file di testo</h2>
<p>Prima di poter leggere da un file, avete bisogno di aprirlo. Aprire un file in Python non potrebbe essere più facile:
<pre class='nd pp'><code>a_file = open('esempi/chinese.txt', encoding='utf-8')</code></pre>
<p>Python possiede una funzione <code>open()</code> predefinita che prende un nome di file come argomento. Qui il nome del file è <code class=pp>'esempi/chinese.txt'</code>. Ci sono cinque cose interessanti da notare a proposito di questo nome di file.
<ol>
<li>Non è solo il nome di un file, ma è la combinazione di un percorso di directory e di un nome di file. Un’ipotetica funzione di apertura di file potrebbe accettare due argomenti — un percorso di directory e un nome di file — ma la funzione <code>open()</code> ne accetta solo uno. In Python, ogni volta che avete bisogno di un “nome di file” potete anche includere un percorso di directory intero o parziale.
<li>Il percorso di directory usa i caratteri di slash, ma io non vi ho detto quale sistema operativo stavo usando. Windows usa i caratteri di backslash per denotare le sottodirectory, mentre Mac OS X e Linux usano i caratteri di slash. Ma in Python gli slash funzionano sempre, persino su Windows.
<li>Il percorso di directory non comincia con uno slash o una lettera di disco, quindi viene chiamato <i>percorso relativo</i>. Potreste chiedervi: relativo a cosa? Sii paziente, cavalletta.
<li>È una stringa. Tutti i sistemi operativi moderni (persino Windows!) usano Unicode per memorizzare i nomi di file e directory. Python 3 supporta pienamente i nomi di percorso in codifiche diverse da <abbr>ASCII</abbr>.
<li>Il file non deve necessariamente trovarsi sul vostro disco locale. Potreste aver montato un disco di rete, oppure quel file potrebbe essere una finzione appartenente a <a href=http://en.wikipedia.org/wiki/Filesystem_in_Userspace>un file system interamente virtuale</a>. Se il vostro computer lo considera un file e può accedervi come a un file, Python può aprirlo.
</ol>
<p>Ma quella invocazione alla funzione <code>open()</code> non si è fermata al nome di file. C’è un altro argomento, chiamato <code>encoding</code>. Oh, cavoli, <a href=stringhe.html#boring-stuff>questo suona spaventosamente familiare</a>.
<h3 id=encoding>La codifica di carattere solleva la sua ripugnante testa</h3>
<p>I byte sono byte, <a href=stringhe.html#byte-arrays>i caratteri sono un’astrazione</a>. Una stringa è una sequenza di caratteri Unicode. Ma un file su disco non è una sequenza di caratteri Unicode, bensì una sequenza di byte. Quindi, se leggete un “file di testo” dal disco, in che modo Python converte quella sequenza di byte in una sequenza di caratteri? Decodifica i byte seguendo un particolare algoritmo di codifica di carattere e restituisce una sequenza di caratteri Unicode (altrimenti nota come una stringa).
<pre>
# Questo esempio è stato creato su Windows. Altre piattaforme potrebbero
# comportarsi in maniera diversa, per le ragioni illustrate di seguito.
<samp class=p>>>> </samp><kbd class=pp>file = open('esempi/chinese.txt')</kbd>
<samp class=p>>>> </samp><kbd class=pp>a_string = file.read()</kbd>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 28: character maps to <undefined></samp>
<samp class=p>>>> </samp></pre>
<aside>La codifica predefinita dipende dalla piattaforma.</aside>
<p>Che cos’è appena successo? Non avete specificato una codifica di carattere, quindi Python è obbligato a usare la codifica predefinita. Qual è la codifica predefinita? Se osservate attentamente la traccia dello stack, potete vedere che l’esecuzione si blocca nel file <code>cp1252.py</code>, indicando che in questo caso Python sta usando <abbr>CP-1252</abbr> come codifica predefinita. (<abbr>CP-1252</abbr> è una codifica comune sui computer che eseguono Microsoft Windows.) L’insieme di caratteri <abbr>CP-1252</abbr> non supporta i caratteri contenuti in questo file, quindi la lettura fallisce con un brutto errore di tipo <code>UnicodeDecodeError</code>.
<p>Ma aspettate, le cose sono peggio di così! La codifica predefinita <em>dipende dalla piattaforma</em>, quindi questo codice <em>potrebbe</em> funzionare sul vostro computer (se la vostra codifica predefinita è <abbr>UTF-8</abbr>), ma poi fallirebbe quando lo distribuite a qualcun altro (la cui codifica predefinita è differente, come <abbr>CP-1252</abbr>).
<blockquote class=note>
<p><span class=u>☞</span>Se avete bisogno di ottenere la codifica di carattere predefinita, importate il modulo <code>locale</code> e invocate <code>locale.getpreferredencoding()</code>. Sul mio portatile Windows la funzione restituisce <code>'cp1252'</code>, ma sulla macchina Linux che ho di sopra restituisce <code>'UTF-8'</code>. Non riesco a mantenere la consistenza nemmeno in casa mia! I vostri risultati potrebbero essere differenti (persino su Windows) a seconda di quale versione del vostro sistema operativo avete installato e di come sono configurate le impostazioni regionali e di linuga. Questo è il motivo per cui è così importante specificare la codifica ogni volta che aprite un file.
</blockquote>
<h3 id=file-objects>Oggetti stream</h3>
<p>Finora tutto quello che sappiamo è che Python ha una funzione predefinita chiamata <code>open()</code>. La funzione <code>open()</code> restituisce un <i>oggetto stream</i>, che è dotato di metodi e attributi per ottenere informazioni su un flusso di caratteri e manipolarlo.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>a_file = open('esempi/chinese.txt', encoding='utf-8')</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.name</kbd> <span class=u>①</span></a>
<samp class=pp>'esempi/chinese.txt'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.encoding</kbd> <span class=u>②</span></a>
<samp class=pp>'utf-8'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.mode</kbd> <span class=u>③</span></a>
<samp class=pp>'r'</samp></pre>
<ol>
<li>L’attributo <code>name</code> riflette il nome che avete passato alla funzione <code>open()</code> quando avete aperto il file. Questo nome non viene normalizzato sotto forma di nome di percorso assoluto.
<li>Similmente, l’attributo <code>encoding</code> riflette la codifica che avete passato alla funzione <code>open()</code>. Se non avete specificato la codifica quando avete aperto il file (cattivi sviluppatori!) allora l’attributo <code>encoding</code> rifletterà il valore restituito da <code>locale.getpreferredencoding()</code>.
<li>L’attributo <code>mode</code> vi dice in quale modalità è stato aperto il file. Potete passare un parametro <var>mode</var> opzionale alla funzione <code>open()</code>. Non avete specificato una modalità quando avete aperto questo file, così Python usa <code>'r'</code> per default, che significa “apri in sola lettura, in modalità testo”. Come vedrete più avanti in questo capitolo, la modalità del file serve a vari scopi: diverse modalità vi permettono di scrivere su un file, aggiungere dati in fondo al file, o aprire un file in modalità binaria (nel qual caso avrete a che fare con byte invece di stringhe).
</ol>
<blockquote class=note>
<p><span class=u>☞</span>La <a href=http://docs.python.org/3.1/library/io.html#module-interface>documentazione per la funzione <code>open()</code></a> elenca tutte le possibili modalità dei file.
</blockquote>
<h3 id=read>Leggere dati da un file di testo</h3>
<p>Dopo aver aperto un file in lettura, a un certo punto vorrete probabilmente leggerne i dati.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>a_file = open('esempi/chinese.txt', encoding='utf-8')</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read()</kbd> <span class=u>①</span></a>
<samp class=pp>'Immersione in Python 是为有经验的程序员编写的一本 Python 书。\n'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read()</kbd> <span class=u>②</span></a>
<samp class=pp>''</samp></pre>
<ol>
<li>Una volta che avete aperto un file (con la codifica corretta), leggere da quel file è semplicemente una questione di invocare il metodo <code>read()</code> dell’oggetto stream. Il risultato è una stringa.
<li>In modo forse abbastanza sorprendente, leggere ancora dal file non solleva un’eccezione. Python non considera una lettura dopo la fine del file come un errore, ma restituisce semplicemente una stringa vuota.
</ol>
<aside>Usate sempre il parametro <code>encoding</code> quando aprite un file.</aside>
<p>E se voleste rileggere un file?
<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read()</kbd> <span class=u>①</span></a>
<samp class=pp>''</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.seek(0)</kbd> <span class=u>②</span></a>
<samp class=pp>0</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read(20)</kbd> <span class=u>③</span></a>
<samp class=pp>'Immersione in Python'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read(1)</kbd> <span class=u>④</span></a>
<samp class=pp>' '</samp>
<samp class=p>>>> </samp><kbd class=pp>a_file.read(1)</kbd>
<samp class=pp>'是'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.tell()</kbd> <span class=u>⑤</span></a>
<samp class=pp>24</samp></pre>
<ol>
<li>Dato che vi trovate ancora alla fine del file, ulteriori invocazioni del metodo <code>read()</code> dell’oggetto stream restituiscono semplicemente una stringa vuota.
<li>Il metodo <code>seek()</code> vi sposta sul byte in una specifica posizione di un file.
<li>Il metodo <code>read()</code> può accettare come parametro opzionale il numero di caratteri da leggere.
<li>Se volete, potete anche leggere un carattere alla volta.
<li>20 + 1 + 1 = … 24?
</ol>
<p>Riproviamo ancora.
<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>a_file.seek(21)</kbd> <span class=u>①</span></a>
<samp class=pp>21</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read(1)</kbd> <span class=u>②</span></a>
<samp class=pp>'是'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.tell()</kbd> <span class=u>③</span></a>
<samp class=pp>24</samp></pre>
<ol>
<li>Spostatevi al 21° byte.
<li>Leggete un carattere.
<li>Ora vi trovate al 24° byte.
</ol>
<p>Ve ne siete già accorti? I metodi <code>seek()</code> e <code>tell()</code> contano sempre i <em>byte</em>, ma dato che avete aperto questo file come testo, il metodo <code>read()</code> conta i <em>caratteri</em>. I caratteri cinesi <a href=stringhe.html#boring-stuff>richiedono più byte per venire codificati in <abbr>UTF-8</abbr></a>. I caratteri inglesi in un file richiedono solo un byte ognuno, quindi potreste essere erroneamente indotti a credere che i metodi <code>seek()</code> e <code>read()</code> stiano contando le stesse cose. Ma questo è vero solo per alcuni caratteri.
<p>Ma aspettate, le cose peggiorano!
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.seek(22)</kbd> <span class=u>①</span></a>
<samp class=pp>22</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read(1)</kbd> <span class=u>②</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
a_file.read(1)
File "C:\Python31\lib\codecs.py", line 300, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf8' codec can't decode byte 0x98 in position 0: unexpected code byte</samp></pre>
<ol>
<li>Spostatevi al 22° byte e provate a leggere un carattere.
<li>Perché questo fallisce? Perché non c’è alcun carattere al 22° byte. Il carattere più vicino comincia al 21° byte (e prosegue per tre byte). Il tentativo di leggere un carattere nel mezzo fallirà con un errore di tipo <code>UnicodeDecodeError</code>.
</ol>
<h3 id=close>Chiudere i file</h3>
<p>I file aperti consumano risorse di sistema, e a seconda della modalità del file altri programmi potrebbero non essere in grado di accedervi. È importante chiudere i file non appena avete finito di lavorarci.
<pre class='nd screen'>
# continua dall'esempio precedente
<samp class=p>>>> </samp><kbd class=pp>a_file.close()</kbd></pre>
<p>Be’, <em>questo</em> è stato un anticlimax.
<p>L’oggetto stream <var>a_file</var> esiste ancora, perché l’invocazione del suo metodo <code>close()</code> non distrugge l’oggetto vero e proprio. Ma non è particolarmente utile.
<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read()</kbd> <span class=u>①</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
a_file.read()
ValueError: I/O operation on closed file.</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.seek(0)</kbd> <span class=u>②</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<pyshell#25>", line 1, in <module>
a_file.seek(0)
ValueError: I/O operation on closed file.</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.tell()</kbd> <span class=u>③</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
a_file.tell()
ValueError: I/O operation on closed file.</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.close()</kbd> <span class=u>④</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.closed</kbd> <span class=u>⑤</span></a>
<samp class=pp>True</samp></pre>
<ol>
<li>Non potete leggere da un file chiuso: questa operazione solleva un’eccezione di tipo <code>IOError</code>.
<li>Non potete neanche effettuare un’operazione di posizionamento su un file chiuso.
<li>Non c’è nessuna posizione corrente in un file chiuso, quindi anche il metodo <code>tell()</code> fallisce.
<li>Forse in modo sorprendente, invocare il metodo <code>close()</code> su un oggetto stream il cui file è stato chiuso <em>non</em> solleva un’eccezione. Semplicemente, non fa nulla.
<li>Gli oggetti stream chiusi hanno almeno un attributo utile: l’attributo <code>closed</code> vi confermerà che il file è chiuso.
</ol>
<h3 id=with>Chiudere i file automaticamente</h3>
<aside><code>try..finally</code> va bene. <code>with</code> va molto meglio.</aside>
<p>Gli oggetti stream hanno un metodo <code>close()</code> esplicito, ma cosa succede se il vostro codice ha un bug e si blocca prima che possiate chiamare <code>close()</code>? Quel file potrebbe teoricamente rimanere aperto per un tempo molto più lungo del necessario. Mentre state effettuando il debug sul vostro computer locale questo non è un grande problema, ma su un server di produzione forse lo è.
<p>Python 2 aveva una soluzione per questo: il blocco <code>try..finally</code>. Questo funziona ancora in Python 3, e potreste vederlo nel codice di altre persone o in codice più vecchio che è stato <a href=caso-di-studio-convertire-chardet-verso-python-3.html>convertito verso Python 3</a>. Ma Python 2.6 ha introdotto una soluzione più pulita, che ora è la soluzione preferita in Python 3: l’istruzione <code>with</code>.
<pre class='nd pp'><code>with open('esempi/chinese.txt', encoding='utf-8') as a_file:
a_file.seek(21)
a_character = a_file.read(1)
print(a_character)</code></pre>
<p>Questo codice invoca <code>open()</code> ma non invoca mai <code>a_file.close()</code>. L’istruzione <code>with</code> comincia un blocco di codice, come un’istruzione <code>if</code> o un ciclo <code>for</code>. All’interno di questo blocco di codice, potete usare la variabile <var>a_file</var> come l’oggetto stream restituito dalla chiamata a <code>open()</code>. Tutti i normali metodi degli oggetti stream sono disponibili — <code>seek()</code>, <code>read()</code>, qualsiasi cosa di cui abbiate bisogno. Quando il blocco <code>with</code> finisce, <em>Python invoca <code>a_file.close()</code> automaticamente</em>.
<p>Ecco la sorpresa: a prescindere da come e quando uscite dal blocco <code>with</code>, Python chiuderà quel file… anche se “uscite” attraverso un’eccezione non gestita. Esatto, anche se il vostro codice solleva un’eccezione e il vostro programma si blocca, quel file verrà chiuso. Garantito.
<blockquote class=note>
<p><span class=u>☞</span>In termini tecnici, l’istruzione <code>with</code> crea un <dfn>contesto di esecuzione</dfn>. In questi esempi, l’oggetto stream agisce come un <dfn>gestore di contesto</dfn>. Python crea l’oggetto stream <var>a_file</var> e gli dice che sta entrando nel contesto di esecuzione. Quando il blocco di codice dell’istruzione <code>with</code> viene completato, Python dice all’oggetto stream che sta uscendo dal contesto di esecuzione e l’oggetto stream invoca il proprio metodo <code>close()</code>. Leggete la sezione <a href=nomi-dei-metodi-speciali.html#context-managers>Classi che possono essere usate in un blocco <code>with</code></a> nell’appendice B per i dettagli.
</blockquote>
<p>Non c’è nulla di specifico per i file nell’istruzione <code>with</code>: è semplicemente un framework generico per creare contesti di esecuzione e informare gli oggetti sul fatto che stanno entrando o uscendo da un contesto di esecuzione. Se l’oggetto in questione è un flusso, allora esegue utili operazioni relative ai file (come chiudere il file automaticamente). Ma questo comportamento è definito nell’oggetto stream, non nell’istruzione <code>with</code>. Ci sono molti altri modi per usare i gestori di contesto che non hanno nulla a che fare con i file. Potete persino creare il vostro gestore, come vedrete più avanti in questo capitolo.
<h3 id=for>Leggere i dati una riga alla volta</h3>
<p>Una “riga” di un file di testo è esattamente quello che pensate che sia — digitate alcune parole, premete <kbd>INVIO</kbd> e ora vi trovate su una nuova riga. Una riga di testo è una sequenza di caratteri delimitata da… cosa, esattamente? Be’, è complicato, perché i file di testo possono usare diversi caratteri per contrassegnare la fine di una riga. Ogni sistema operativo ha le proprie convenzioni. Alcuni usano un carattere di ritorno a capo, altri usano un carattere di fine riga e alcuni usano entrambi i caratteri al termine di ogni riga.
<p>Ora tirate un sospiro di sollievo, perché <em>Python gestisce i caratteri di fine riga automaticamente</em> per default. Se dite “voglio leggere questo file di testo una riga alla volta”, Python scoprirà che tipo di carattere di fine riga viene usato dal file di testo e ogni cosa funzionerà a dovere.
<blockquote class=note>
<p><span class=u>☞</span>Se avete bisogno di un controllo più fine su quali caratteri vengono considerati per indicare la fine di una riga, potete passare il parametro opzionale <code>newline</code> alla funzione <code>open()</code>. Leggete la <a href=http://docs.python.org/3.1/library/io.html#module-interface>documentazione della funzione <code>open()</code></a> per conoscere tutti i dettagli.
</blockquote>
<p>Quindi, come fate effettivamente a farlo? Leggere un file una riga alla volta, intendo. È talmente semplice da essere bello.
<p class=d>[<a href=esempi/oneline.py>scarica <code>oneline.py</code></a>]
<pre class=pp><code>line_number = 0
<a>with open('esempi/favorite-people.txt', encoding='utf-8') as a_file: <span class=u>①</span></a>
<a> for a_line in a_file: <span class=u>②</span></a>
line_number += 1
<a> print('{:>4} {}'.format(line_number, a_line.rstrip())) <span class=u>③</span></a></code></pre>
<ol>
<li>Usando <a href=#with>il pattern <code>with</code></a>, aprite il file in sicurezza e lasciate che Python lo chiuda per voi.
<li>Per leggere un file una riga alla volta usate un ciclo <code>for</code>. Questo è tutto. Oltre a possedere metodi espliciti come <code>read()</code>, <em>l’oggetto stream è anche un <a href=iteratori.html>iteratore</a></em> che restituisce una singola riga ogni volta che gli chiedete un valore.
<li>Usando <a href=stringhe.html#formatting-strings>il metodo <code>format()</code> delle stringhe</a>, potete stampare il numero di riga e la riga stessa. La specifica di formato <code>{:>4}</code> significa “stampa l’argomento giustificato a destra all’interno di 4 spazi”. La variabile <var>a_line</var> contiene la riga completa, ritorni a capo e tutto quanto. Il metodo <code>rstrip()</code> delle stringhe rimuove gli spazi bianchi in coda, compresi i caratteri di ritorno a capo.
</ol>
<pre class=screen>
<samp class=p>you@localhost:~/diveintopython3$ </samp><kbd class=pp>python3 esempi/oneline.py</kbd>
<samp> 1 Dora
2 Ethan
3 Wesley
4 John
5 Anne
6 Mike
7 Chris
8 Sarah
9 Alex
10 Lizzie</samp></pre>
<blockquote class=pf>
<p>Avete ottenuto questo errore?
<pre class='nd screen'>
<samp class=p>you@localhost:~/diveintopython3$ </samp><kbd class=pp>python3 esempi/oneline.py</kbd>
<samp class=traceback>Traceback (most recent call last):
File "esempi/oneline.py", line 4, in <module>
print('{:>4} {}'.format(line_number, a_line.rstrip()))
ValueError: zero length field name in format</samp></pre>
<p>Se è così, probabilmente state usando Python 3.0. Dovreste davvero aggiornarvi a Python 3.1.
<p>Python 3.0 supportava la formattazione di stringhe, ma solo con <a href=stringhe.html#formatting-strings>specifiche di formato esplicitamente numerate</a>. Python 3.1 vi permette di omettere gli indici degli argomenti nelle vostre specifiche di formato. Per fare un confronto, ecco la versione compatibile con Python 3.0:
<pre class='pp nd'><code>print('{<mark>0</mark>:>4} {<mark>1</mark>}'.format(line_number, a_line.rstrip()))</code></pre>
</blockquote>
<p class=a>⁂
<h2 id=writing>Scrivere sui file di testo</h2>
<aside>Dovete solo aprire un file e cominciare a scrivere.</aside>
<p>Potete scrivere sui file in modo simile a come li leggete. Prima aprite un file e ottenete un oggetto stream, poi usate i metodi dell’oggetto stream per scrivere dati sul file, infine chiudete il file.
<p>Per aprire un file in modo da scriverci sopra, usate la funzione <code>open()</code> e specificate la modalità di scrittura. Ci sono due modalità per scrivere su un file.
<ul>
<li>La modalità di “scrittura” sovrascriverà il file. Passate <code>mode='w'</code> alla funzione <code>open()</code>.
<li>La modalità di “aggiunta” aggiungerà i dati alla fine del file. Passate <code>mode='a'</code> alla funzione <code>open()</code>.
</ul>
<p>In entrambe le modalità, il file verrà automaticamente creato se non esiste già, quindi non c’è mai bisogno di alcuna funzione che, nel caso il file non esista, tergiversi creando un nuovo file vuoto giusto in modo che voi possiate aprirlo per la prima volta. Dovete solo aprire un file e cominciare a scrivere.
<p>Dovreste sempre chiudere un file non appena avete finito di scriverlo, in modo da rilasciare il puntatore al file e assicurarvi che i dati vengano effettivamente scritti sul disco. Come quando leggete i dati da un file, potete invocare il metodo <code>close()</code> dell’oggetto stream, oppure potete usare l’istruzione <code>with</code> e lasciare che Python chiuda il file per voi. Scommetto che riuscite a indovinare quale tecnica vi consiglio di usare.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>with open('test.log', mode='w', encoding='utf-8') as a_file:</kbd> <span class=u>①</span></a>
<a><samp class=p>... </samp><kbd class=pp> a_file.write('il test ha funzionato')</kbd> <span class=u>②</span></a>
<samp class=p>>>> </samp><kbd class=pp>with open('test.log', encoding='utf-8') as a_file:</kbd>
<samp class=p>... </samp><kbd class=pp> print(a_file.read())</kbd>
<samp class=pp>il test ha funzionato</samp>
<a><samp class=p>>>> </samp><kbd class=pp>with open('test.log', mode='a', encoding='utf-8') as a_file:</kbd> <span class=u>③</span></a>
<samp class=p>... </samp><kbd class=pp> a_file.write('ancora una volta')</kbd>
<samp class=p>>>> </samp><kbd class=pp>with open('test.log', encoding='utf-8') as a_file:</kbd>
<samp class=p>... </samp><kbd class=pp> print(a_file.read())</kbd>
<a><samp class=pp>il test ha funzionatoancora una volta</samp> <span class=u>④</span></a></pre>
<ol>
<li>Cominciate baldanzosamente creando il nuovo file <code>test.log</code> (oppure sovrascrivendo il file esistente) e aprendo il file per la scrittura. Usare il parametro <code>mode='w'</code> significa aprire il file in scrittura. Sì, questo è tanto pericoloso quanto sembra. Spero che non vi interessassero i contenuti precedenti di quel file (se c’erano) perché quei dati ora sono scomparsi.
<li>Potete aggiungere dati al file appena aperto con il metodo <code>write()</code> dell’oggetto stream restituito dalla funzione <code>open()</code>. Dopo che il blocco <code>with</code> si è concluso, Python chiude automaticamente il file.
<li>È stato così divertente, facciamolo ancora. Ma questa volta con <code>mode='a'</code>, per aggiungere i dati in coda al file invece di sovrascriverlo. Questa modalità non danneggerà <em>mai</em> i contenuti esistenti del file.
<li>Il file <code>test.log</code> ora contiene sia la riga originale che avete scritto sia la seconda riga che avete aggiunto. Notate anche che i caratteri di ritorno a capo e di fine riga non sono inclusi. Dato che entrambe le volte non li avete scritti esplicitamente sul file, il file non li contiene. Potete scrivere un carattere di ritorno a capo usando <code>'\r'</code> e/o un carattere di fine riga usando <code>'\n'</code>. Dato che non avete fatto né l’una né l’altra cosa, tutto quello che avete scritto sul file è finito su un’unica riga.
</ol>
<h3 id=encoding-again>La codifica di carattere, ancora una volta</h3>
<p>Avete notato il parametro <code>encoding</code> che è stato passato alla funzione <code>open()</code> mentre stavate <a href=#writing>aprendo il file in scrittura</a>? È importante, non dimenticatevelo mai! Come avete visto all’inizio di questo capitolo, i file non contengono <i>stringhe</i>, ma contengono <i>byte</i>. Leggere una “stringa” da un file di testo funziona solo perché avete detto a Python quale codifica usare per leggere un flusso di byte e convertirlo in una stringa. Scrivere testo su un file presenta il problema inverso. Non potete scrivere caratteri su un file: <a href=stringhe.html#byte-arrays>i caratteri sono un’astrazione</a>. Per essere in grado di scrivere sul file, Python ha bisogno di convertire la vostra stringa in una sequenza di byte. L’unico modo per essere sicuri di effettuare la conversione corretta è quello di specificare il parametro <code>encoding</code> quando aprite il file in scrittura.
<p class=a>⁂
<h2 id=binary>File binari</h2>
<p class=ss><img src=esempi/beauregard.jpg alt='il mio cane Beauregard' width=100 height=100>
<p>Non tutti i file contengono testo. Alcuni contengono immagini del mio cane.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>an_image = open('esempi/beauregard.jpg', mode='rb')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>an_image.mode</kbd> <span class=u>②</span></a>
<samp class=pp>'rb'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>an_image.name</kbd> <span class=u>③</span></a>
<samp class=pp>'esempi/beauregard.jpg'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>an_image.encoding</kbd> <span class=u>④</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: '_io.BufferedReader' object has no attribute 'encoding'</samp></pre>
<ol>
<li>Aprire un file in modalità binaria è semplice ma sottile. L’unica differenza rispetto ad aprirlo in modalità testo è che il parametro <code>mode</code> contiene un carattere <code>'b'</code>.
<li>L’oggetto stream che ottenete dall’apertura del file in modalità binaria è dotato di molti degli stessi attributi, incluso l’attributo <code>mode</code> che riflette il parametro <code>mode</code> passato alla funzione <code>open()</code>.
<li>Gli oggetti stream binari hanno anche un attributo <code>name</code>, esattamente come gli oggetti stream testuali.
<li>Ecco una differenza, però: un oggetto stream binario non ha l’attributo <code>encoding</code>. Questo ha senso, giusto? State leggendo (o scrivendo) byte, non stringhe, quindi Python non ha nessuna conversione da fare. Quello che ottenete da un file binario è esattamente quello che ci mettete dentro, e nessuna conversione è necessaria.
</ol>
<p>Vi ho detto che state leggendo byte? Oh sì, è proprio così.
<pre class=screen>
# continua dall'esempio precedente
<samp class=p>>>> </samp><kbd class=pp>an_image.tell()</kbd>
<samp class=pp>0</samp>
<a><samp class=p>>>> </samp><kbd class=pp>data = an_image.read(3)</kbd> <span class=u>①</span></a>
<samp class=p>>>> </samp><kbd class=pp>data</kbd>
<samp class=pp>b'\xff\xd8\xff'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>type(data)</kbd> <span class=u>②</span></a>
<samp class=pp><class 'bytes'></samp>
<a><samp class=p>>>> </samp><kbd class=pp>an_image.tell()</kbd> <span class=u>③</span></a>
<samp class=pp>3</samp>
<samp class=p>>>> </samp><kbd class=pp>an_image.seek(0)</kbd>
<samp class=pp>0</samp>
<samp class=p>>>> </samp><kbd class=pp>data = an_image.read()</kbd>
<samp class=p>>>> </samp><kbd class=pp>len(data)</kbd>
<samp class=pp>3150</samp></pre>
<ol>
<li>Come con i file di testo, potete leggere i file binari un pezzo alla volta. Ma c’è una differenza cruciale…
<li>…state leggendo byte, non stringhe. Dato che avete aperto il file in modalità binaria, il metodo <code>read()</code> accetta <em>il numero di byte da leggere</em>, non il numero di caratteri.
<li>Questo significa che non c’è mai <a href=#read>uno scarto inatteso</a> tra il numero che avete passato al metodo <code>read()</code> e l’indice di posizione che ottenete dal metodo <code>tell()</code>. Il metodo <code>read()</code> legge byte e i metodi <code>seek()</code> e <code>tell()</code> tengono traccia del numero dei byte letti. Per quanto riguarda i file binari, si trovano sempre in accordo.
</ol>
<p class=a>⁂
<h2 id=file-like-objects>Oggetti stream per sorgenti diverse dai file</h2>
<aside>Per leggere da un falso file vi basta chiamare <code>read()</code>.</aside>
<p>Immaginate di scrivere una libreria e che una delle vostre funzioni di libreria debba leggere alcuni dati da un file. La funzione potrebbe semplicemente prendere il nome di file sotto forma di stringa, aprire il file in lettura, leggerlo e chiuderlo prima di uscire. Ma non dovreste fare in questo modo. Invece, la vostra <abbr>API</abbr> dovrebbe accettare <em>un oggetto stream arbitrario</em>.
<p>Nel caso più semplice, un oggetto stream è qualsiasi cosa con un metodo <code>read()</code> che accetta un parametro opzionale <var>size</var> e restituisce una stringa. Quando viene chiamato senza parametro <var>size</var>, il metodo <code>read()</code> dovrebbe leggere tutto quello che c’è da leggere dalla sorgente in ingresso e restituire tutti i dati come un singolo valore. Quando viene chiamato con il parametro <var>size</var>, il metodo legge tanti dati quanti sono stati indicati e restituisce quei dati. Quando viene nuovamente chiamato, riprende da dove era rimasto e restituisce il blocco di dati successivo.
<p>Questo sembra esattamente l’oggetto stream che ottenete aprendo un file reale. La differenza è che <em>non vi limitate ai file reali</em>. La sorgente in ingresso che viene “letta” potrebbe essere qualsiasi cosa: una pagina web, una stringa in memoria, persino l’uscita di un altro programma. Fino a quando le vostre funzioni accettano un oggetto stream e invocano semplicemente il metodo <code>read()</code> dell’oggetto, potete operare su qualsiasi sorgente in ingresso che si comporta come un file, senza usare codice specifico per gestire ogni singolo tipo di ingresso.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>a_string = 'PapayaWhip è il nuovo nero.'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>import io</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>a_file = io.StringIO(a_string)</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read()</kbd> <span class=u>③</span></a>
<samp class=pp>'PapayaWhip è il nuovo nero.'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read()</kbd> <span class=u>④</span></a>
<samp class=pp>''</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.seek(0)</kbd> <span class=u>⑤</span></a>
<samp class=pp>0</samp>
<a><samp class=p>>>> </samp><kbd class=pp>a_file.read(10)</kbd> <span class=u>⑥</span></a>
<samp class=pp>'PapayaWhip'</samp>
<samp class=p>>>> </samp><kbd class=pp>a_file.tell()</kbd>
<samp class=pp>10</samp>
<samp class=p>>>> </samp><kbd class=pp>a_file.seek(16)</kbd>
<samp class=pp>16</samp>
<samp class=p>>>> </samp><kbd class=pp>a_file.read()</kbd>
<samp class=pp>'nuovo nero.'</samp></pre>
<ol>
<li>Il modulo <code>io</code> definisce la classe <code>StringIO</code>, che potete usare per trattare una stringa in memoria come un file.
<li>Per creare un oggetto stream a partire da una stringa, create un’istanza della classe <code>io.StringIO()</code> e passatele la stringa che volete usare come dati del vostro “file”. Ora avete un oggetto stream e potete eseguire qualsiasi operazione relativa ai flussi su di esso.
<li>Invocare il metodo <code>read()</code> “legge” l’intero “file” e nel caso di un oggetto <code>StringIO</code> restituisce semplicemente la stringa originale.
<li>Esattamente come con un vero file, invocare ancora il metodo <code>read()</code> restituisce una stringa vuota.
<li>Potete esplicitamente tornare all’inizio della stringa con un’operazione di posizionamento, esattamente come accade per un vero file, usando il metodo <code>seek()</code> dell’oggetto <code>StringIO</code>.
<li>Potete anche leggere la stringa a blocchi, passando un parametro <var>size</var> al metodo <code>read()</code>.
</ol>
<blockquote class=note>
<p><span class=u>☞</span><code>io.StringIO</code> vi permette di trattare una stringa come un file di testo. Esiste anche una classe <code>io.BytesIO</code> che vi permette di trattare un array di byte come un file binario.
</blockquote>
<h3 id=gzip>Lavorare con i file compressi</h3>
<p>La libreria standard di Python contiene moduli che supportano le operazioni di lettura e scrittura sui file compressi. Esistono un certo numero di schemi di compressione differenti; i due più popolari su sistemi diversi da Windows sono <a href=http://docs.python.org/3.1/library/gzip.html>gzip</a> e <a href=http://docs.python.org/3.1/library/bz2.html>bzip2</a>. (Potreste anche avere incontrato <a href=http://docs.python.org/3.1/library/zipfile.html>archivi PKZIP</a> e <a href=http://docs.python.org/3.1/library/tarfile.html>archivi GNU Tar</a>. Python possiede moduli anche per quelli.)
<p>Il modulo <code>gzip</code> vi permette di creare un oggetto stream per leggere o scrivere un file compresso con gzip. L’oggetto stream che vi fornisce supporta il metodo <code>read()</code> (se lo avete aperto in lettura) o il metodo <code>write()</code> (se lo avete aperto in scrittura). Questo significa che potete usare i metodi che avete già imparato per i normali file allo scopo di <em>leggere o scrivere direttamente un file compresso con gzip</em> senza creare un file temporaneo dove memorizzare i dati decompressi.
<p>Come bonus aggiuntivo, il modulo supporta anche l’istruzione <code>with</code>, quindi potete lasciare che sia Python a chiudere automaticamente il vostro file gzip quando avete finito di lavorare.
<pre class='nd screen'>
<samp class=p>you@localhost:~$ </samp><kbd>python3</kbd>
<samp class=p>>>> </samp><kbd class=pp>import gzip</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>with gzip.open('out.log.gz', mode='wb') as z_file:</kbd> <span class=u>①</span></a>
<samp class=p>... </samp><kbd class=pp> z_file.write('Camminare per nove miglia non è uno scherzo, specialmente se piove.'.encode('utf-8'))</kbd>
<samp class=p>... </samp>
<samp class=p>>>> </samp><kbd class=pp>exit()</kbd>
<a><samp class=p>you@localhost:~$ </samp><kbd>ls -l out.log.gz</kbd> <span class=u>②</span></a>
<samp>-rw-r--r-- 1 you you 93 2009-07-19 14:29 out.log.gz</samp>
<a><samp class=p>you@localhost:~$ </samp><kbd>gunzip out.log.gz</kbd> <span class=u>③</span></a>
<a><samp class=p>you@localhost:~$ </samp><kbd>cat out.log</kbd> <span class=u>④</span></a>
<samp>Camminare per nove miglia non è uno scherzo, specialmente se piove.</samp></pre>
<ol>
<li>Dovreste sempre aprire i file compressi con gzip in modalità binaria. (Notate il carattere <code>'b'</code> nell’argomento <code>mode</code>.)
<li>Ho realizzato questo esempio su Linux. Se non avete familiarità con la riga di comando, sappiate che questo comando mostra “l’elenco esteso” del file gzip che avete appena creato nella Shell Python. Questo elenco mostra che il file esiste (bene) e che è di 93 byte. Questo file è in realtà più grande della stringa con cui avete cominciato! Il formato gzip include un’intestazione di lunghezza fissa che contiene alcuni metadati sul file, quindi è inefficiente per file estremamente piccoli.
<li>Il comando <code>gunzip</code> (pronunciato “gee-unzip” in inglese) decomprime il file e memorizza i contenuti in un nuovo file chiamato con lo stesso nome del file compresso ma senza l’estensione <code>.gz</code>.
<li>Il comando <code>cat</code> mostra i contenuti di un file. Questo file contiene la stringa che avevate originariamente scritto direttamente sul file compresso <code>out.log.gz</code> dall’interno della Shell Python.
</ol>
<blockquote class=pf>
<p>Avete ottenuto questo errore?
<pre class='nd screen'>
<samp class=p>>>> </samp><kbd class=pp>with gzip.open('out.log.gz', mode='wb') as z_file:</kbd>
<samp class=p>... </samp><kbd class=pp> z_file.write('Camminare per nove miglia non è [...] se piove.'.encode('utf-8'))</kbd>
<samp class=p>... </samp>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'GzipFile' object has no attribute '__exit__'</samp></pre>
<p>Se è così, probabilmente state usando Python 3.0. Dovreste davvero aggiornarvi a Python 3.1.
<p>Il modulo <code>gzip</code> incluso in Python 3.0 non supportava l’uso di un file gzip come gestore di contesto. Python 3.1 ha aggiunto la possibilità di usare oggetti file gzip in un’istruzione <code>with</code>.
</blockquote>
<p class=a>⁂
<h2 id=stdio>I canali standard di ingresso, uscita ed errore</h2>
<aside><code>sys.stdin</code>, <code>sys.stdout</code>, <code>sys.stderr</code>.</aside>
<p>I guru della riga di comando conoscono già il concetto di canale standard di ingresso, uscita ed errore. Questa sezione è per tutti gli altri.
<p>I canali di uscita e di errore standard (comunemente abbreviati come <code>stdout</code> e <code>stderr</code>) sono canali predefiniti in ogni sistema di tipo <abbr>UNIX</abbr>, compresi Mac OS X e Linux. Quando invocate la funzione <code>print()</code>, quello che state stampando viene inviato al canale <code>stdout</code>. Quando il vostro programma si blocca e stampa una traccia dello stack di esecuzione, ciò che viene stampato è inviato al canale <code>stderr</code>. Per default, entrambi i canali sono collegati alla finestra di terminale in cui state lavorando, così quando il vostro programma stampa qualcosa vedrete il messaggio nella vostra finestra di terminale, e quando un programma si blocca vedrete anche la traccia dello stack di esecuzione nella stessa finestra. Nella Shell Python grafica, i canali <code>stdout</code> e <code>stderr</code> corrispondono per default alla vostra “finestra interattiva”.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>for i in range(3):</kbd>
<a><samp class=p>... </samp><kbd class=pp> print('PapayaWhip')</kbd> <span class=u>①</span></a>
<samp>PapayaWhip
PapayaWhip
PapayaWhip</samp>
<samp class=p>>>> </samp><kbd class=pp>import sys</kbd>
<samp class=p>>>> </samp><kbd class=pp>for i in range(3):</kbd>
<a><samp class=p>... </samp><kbd class=pp>sys.stdout.write('è il')</kbd> <span class=u>②</span></a>
<samp>è ilè ilè il</samp>
<samp class=p>>>> </samp><kbd class=pp>for i in range(3):</kbd>
<a><samp class=p>... </samp><kbd class=pp>sys.stderr.write('nuovo nero')</kbd> <span class=u>③</span></a>
<samp>nuovo neronuovo neronuovo nero</samp></pre>
<ol>
<li>La funzione <code>print()</code>, in un ciclo. Niente di sorprendente qui.
<li><code>stdout</code> è definito nel modulo <code>sys</code> ed è un <a href=#file-like-objects>oggetto stream</a>. Invocare la sua funzione <code>write()</code> stamperà qualunque stringa le passiate. In effetti, questo è ciò che la funzione <code>print()</code> fa in realtà: aggiunge un ritorno a capo alla fine della stringa che state stampando e invoca <code>sys.stdout.write()</code>.
<li>Nel caso più semplice, <code>sys.stdout</code> e <code>sys.stderr</code> inviano i loro dati nello stesso posto: un <abbr>IDE</abbr> Python (se ne state usando uno) oppure il terminale (se state eseguendo Python dalla riga di comando). Come il canale di uscita standard, il canale di errore standard non aggiunge i ritorni a capo per voi, perciò se li volete dovrete scrivere i caratteri di ritorno a capo.
</ol>
<p><code>sys.stdout</code> e <code>sys.stderr</code> sono oggetti stream, ma sono a sola scrittura. Il tentativo di invocare il loro metodo <code>read()</code> solleverà sempre un’eccezione di tipo <code>IOError</code>.
<pre class='nd screen'>
<samp class=p>>>> </samp><kbd class=pp>import sys</kbd>
<samp class=p>>>> </samp><kbd class=pp>sys.stdout.read()</kbd>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: not readable</samp></pre>
<h3 id=redirect>Redirigere il canale standard di uscita</h3>
<p><code>sys.stdout</code> e <code>sys.stderr</code> sono oggetti stream, sebbene supportino solo le operazioni di scrittura. Ma non sono costanti, bensì variabili. Questo significa che potete assegnare loro un nuovo valore — qualsiasi altro oggetto stream — per redirigere i loro messaggi.
<p class=d>[<a href=esempi/stdout.py>scarica <code>stdout.py</code></a>]
<pre class=pp><code>import sys
class RedirectStdoutTo:
def __init__(self, out_new):
self.out_new = out_new
def __enter__(self):
self.out_old = sys.stdout
sys.stdout = self.out_new
def __exit__(self, *args):
sys.stdout = self.out_old
print('A')
with open('out.log', mode='w', encoding='utf-8') as a_file, RedirectStdoutTo(a_file):
print('B')
print('C')</code></pre>
<p>Controllate:
<pre class='nd screen'>
<samp class=p>you@localhost:~/diveintopython3/esempi$ </samp><kbd>python3 stdout.py</kbd>
<samp>A
C</samp>
<samp class=p>you@localhost:~/diveintopython3/esempi$ </samp><kbd>cat out.log</kbd>
<samp>B</samp></pre>
<blockquote class=pf>
<p>Avete ottenuto questo errore?
<pre class='nd screen'>
<samp class=p>you@localhost:~/diveintopython3/esempi$ </samp><kbd class=pp>python3 stdout.py</kbd>
<samp class=traceback> File "stdout.py", line 15
with open('out.log', mode='w', encoding='utf-8') as a_file, RedirectStdoutTo(a_file):
^
SyntaxError: invalid syntax</samp></pre>
<p>Se è così, probabilmente state usando Python 3.0. Dovreste davvero aggiornarvi a Python 3.1.
<p>Python 3.0 supportava l’istruzione <code>with</code>, ma ogni istruzione poteva usare solo un gestore di contesto. Python 3.1 vi permette di concatenare molteplici gestori di contesto in una singola istruzione <code>with</code>.
</blockquote>
<p>Esaminiamo l’ultima parte per prima.
<pre class=pp><code>print('A')
with open('out.log', mode='w', encoding='utf-8') as a_file, RedirectStdoutTo(a_file):
print('B')
print('C')</code></pre>
<p>Questa è un’istruzione <code>with</code> complicata. Permettetemi di riscriverla in qualcosa di più riconoscibile.
<pre class=pp><code>with open('out.log', mode='w', encoding='utf-8') as a_file:
with RedirectStdoutTo(a_file):
print('B')</code></pre>
<p>Come mostrato dalla riscrittura, avete effettivamente <em>due</em> istruzioni <code>with</code>, una annidata nell’ambito dell’altra. L’istruzione <code>with</code> “esterna” dovrebbe esservi familiare ormai: apre in scrittura un file di testo codificato in <abbr>UTF-8</abbr> chiamato <code>out.log</code> e assegna l’oggetto stream a una variabile chiamata <var>a_file</var>. Ma questa non è l’unica cosa strana qui.
<pre class='nd pp'><code>with RedirectStdoutTo(a_file):</code></pre>
<p>Dov’è la clausola <code>as</code>? L’istruzione <code>with</code> in effetti non ne richiede una. Esattamente come potete invocare una funzione e ignorare il suo valore di ritorno, potete avere un’istruzione <code>with</code> che non assegna il contesto di <code>with</code> a una variabile. In questo caso, siete solo interessati agli effetti collaterali del contesto <code>RedirectStdoutTo</code>.
<p>Quali sono questi effetti collaterali? Diamo un’occhiata all’interno della classe <code>RedirectStdoutTo</code>. Questa classe è un <a href=nomi-dei-metodi-speciali.html#context-managers>gestore di contesto</a> personalizzato. Qualsiasi classe può diventare un gestore di contesto definendo due <a href=iteratori.html#a-fibonacci-iterator>metodi speciali</a>: <code>__enter__()</code> ed <code>__exit__()</code>.
<pre class=pp><code>class RedirectStdoutTo:
<a> def __init__(self, out_new): <span class=u>①</span></a>
self.out_new = out_new
<a> def __enter__(self): <span class=u>②</span></a>
self.out_old = sys.stdout
sys.stdout = self.out_new
<a> def __exit__(self, *args): <span class=u>③</span></a>
sys.stdout = self.out_old</code></pre>
<ol>
<li>Il metodo <code>__init__()</code> viene chiamato immediatamente dopo che un’istanza è stata creata. Accetta come parametro l’oggetto stream che volete usare come canale standard di uscita per la vita del contesto. Questo metodo non fa altro che salvare l’oggetto stream in una variabile di istanza in modo che altri metodi possano usarlo più tardi.
<li>Il metodo <code>__enter__()</code> è un <a href=iteratori.html#a-fibonacci-iterator>metodo speciale per le classi</a>; l’interprete Python lo invoca quando entra in un contesto (cioè all’inizio dell’istruzione <code>with</code>). Questo metodo salva il valore corrente di <code>sys.stdout</code> in <var>self.out_old</var>, poi redirige il canale standard di uscita assegnando <var>self.out_new</var> a <var>sys.stdout</var>.
<li>Il metodo <code>__exit__()</code> è un altro metodo speciale per le classi; l’interprete Python lo invoca quando esce da un contesto (cioè alla fine dell’istruzione <code>with</code>). Questo metodo ripristina il canale standard di uscita al suo valore originale assegnando il valore salvato in <var>self.out_old</var> a <var>sys.stdout</var>.
</ol>
<p>Riepilogando:
<pre class=pp><code>
<a>print('A') <span class=u>①</span></a>
<a>with open('out.log', mode='w', encoding='utf-8') as a_file, RedirectStdoutTo(a_file): <span class=u>②</span></a>
<a> print('B') <span class=u>③</span></a>
<a>print('C') <span class=u>④</span></a></code></pre>
<ol>
<li>Questo stamperà nella “finestra interattiva” del vostro <abbr>IDE</abbr> (o sul terminale, se avete invocato lo script dalla riga di comando).
<li>Questa <a href=#with>istruzione <code>with</code></a> prende <em>una lista di contesti separati da virgole</em> che agisce come una serie di blocchi <code>with</code> annidati. Il primo contesto nella lista è il blocco “più esterno”, l’ultimo è il blocco “più interno”. Il primo contesto apre un file, il secondo contesto redirige <code>sys.stdout</code> all’oggetto stream che è stato creato nel primo contesto.
<li>Dato che questa funzione <code>print()</code> viene invocata nei contesti creati dall’istruzione <code>with</code>, non stamperà sullo schermo ma scriverà sul file <code>out.log</code>.
<li>Il blocco di codice <code>with</code> è finito. Python ha detto a tutti i gestori di contesto di fare qualunque cosa facciano al momento di uscire da un contesto. I gestori di contesto formano una pila. Uscendo, il secondo contesto ha ripristinato il valore originale di <code>sys.stdout</code>, poi il primo contesto ha chiuso il file chiamato <code>out.log</code>. Dato che il canale standard di uscita è stato ripristinato al suo valore originale, l’invocazione della funzione <code>print()</code> stamperà ancora una volta sullo schermo.
</ol>
<p>La redirirezione del canale standard di errore funziona esattamente allo stesso modo, usando <code>sys.stderr</code> anziché <code>sys.stdout</code>.
<p class=a>⁂
<h2 id=furtherreading>Letture di approfondimento</h2>
<ul>
<li><a href=http://docs.python.org/py3k/tutorial/inputoutput.html#reading-and-writing-files>Leggere e scrivere i file</a> nel tutorial su Python.org
<li><a href=http://docs.python.org/3.1/library/io.html>Il modulo <code>io</code></a>
<li><a href=http://docs.python.org/3.1/library/stdtypes.html#file-objects>Gli oggetti stream</a>
<li><a href=http://docs.python.org/3.1/library/stdtypes.html#context-manager-types>I tipi di gestori di contesto</a>
<li><a href=http://docs.python.org/3.1/library/sys.html#sys.stdout><code>sys.stdout</code> e <code>sys.stderr</code></a>
<li><a href=http://en.wikipedia.org/wiki/Filesystem_in_Userspace><abbr>FUSE</abbr> su Wikipedia</a>
</ul>
<p class=v><a href=refactoring.html rel=prev title='indietro a “Refactoring”'><span class=u>☜</span></a> <a href=xml.html rel=next title='avanti a “XML”'><span class=u>☞</span></a>
<p class=c>© 2001–10 <a href=about.html>Mark Pilgrim</a><br>
© 2009–10 <a href=informazioni-sulla-traduzione.html>Giulio Piancastelli</a> per la traduzione italiana
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>