Subsections


7. Input ed output

Ci sono parecchi modi per mostrare l'output di un programma; i dati possono essere stampati in una forma leggibile, o scritti in un file per usi futuri. Questo capitolo tratterà alcune delle possibilità.


7.1 Formattazione avanzata dell'output

Fino a qui si sono visti due modi di scrivere valori: le espressioni e l'istruzione print. Un terzo modo è usare il metodo write() degli oggetti file; si può fare riferimento al file di standard output come sys.stdout. Si veda la Libreria di riferimento per ulteriori informazioni.

Spesso si vorrà avere un controllo sulla formattazione dell'output che vada aldilà dello stampare semplicemente dei valori separati da spazi. Ci sono due modi per formattare l'output; il primo è fare da sé tutto il lavoro di gestione delle stringhe; usando le operazioni di affettamento e concatenamento si possono creare tutti i layout che si vogliono. Il modulo standard string contiene alcuni utili operatori di riempimento (NdT: padding) di stringhe ad una colonna di ampiezza data; questi verranno trattati brevemente. Il secondo modo è usare l'operatore % con una stringa come argomento a sinistra. % interpreta l'argomento di sinistra come una stringa di formato nello stile della funzione C sprintf() che dev'essere applicata all'argomento a destra, e restituisce la stringa risultante.

Beninteso, rimane un problema: come si fa a convertire valori in stringhe? Fortunatamente, Python ha un modo per convertire un qualsiasi valore in una stringa: lo si passa alla funzione repr() o str(). Gli apici inversi (` `) sono equivalenti a repr(), ma l'uso di questa forma è scoraggiato.

La funzione str() restituisce la rappresentazione del valore in termini umanamente comprensibili, mentre repr() genera la rappresentazione del valore comprensibile dall'interprete (o solleverà un'eccezione del tipo SyntaxError se questa non ha una sintassi equivalente). Per oggetti che non hanno una specifica rappresentazione in termini umanamente comprensibili, str() restituirà lo stesso valore di repr(). Molti valori, simili a quelli numerici o a strutture come liste e dizionari, hanno la stessa rappresentazione usando le due funzioni. Nello specifico, stringhe e numeri in virgola mobile, hanno due distinte rappresentazioni.

Alcuni esempi:

>>> s = 'Ciao, mondo.'
>>> str(s)
'Ciao, mondo.'
>>> repr(s)
"'Ciao, mondo.'"
>>> str(0.1)
'0.1'
>>> repr(0.1)
'0.10000000000000001'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'Il valore di x è ' + repr(x) + ' e y è ' + repr(y) + '...'
>>> print s
Il valore di x è 32.5 e y è 40000...
>>> # repr() aggiunge gli apici di stringa e le barre rovesciate:
... ciao = 'ciao, mondo\n'
>>> ciaos = repr(ciao)
>>> print ciaos
'ciao, mondo\n'
>>> # L'argomento di repr() può essere anche un oggetto Python:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"
>>> # Gli apici inversi possono essere convenienti in sessioni interattive:
... `x, y, ('spam', 'eggs')`
"(32.5, 40000, ('spam', 'eggs'))"

Ecco due modi di scrivere una tabella di quadrati e cubi:

>>> for x in range(1, 11):
...     print repr(x).rjust(2), repr(x*x).rjust(3),
...     # Si noti la virgola in coda sulla riga precedente
...     print repr(x*x*x).rjust(4)
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000
>>> for x in range(1,11):
...     print '%2d %3d %4d' % (x, x*x, x*x*x)
... 
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

Si noti che uno spazio fra le colonne è stato aggiunto per il modo in cui lavora print: viene sempre inserito uno spazio tra i suoi argomenti.

Questa è una dimostrazione del metodo rjust() di oggetti stringa, che giustifica a destra una stringa in un campo di ampiezza data, riempiendola di spazi a sinistra. Questo metodo non scrive niente ma restituisce una nuova stringa. Se la stringa di input è troppo lunga non viene troncata ma restituita intatta; questo potrebbe scombussolare l'allineamento delle colonne desiderato, ma di solito questo è preferibile all'alternativa, cioè riportare un valore menzognero. Se davvero si desidera il troncamento si può sempre aggiungere un'operazione di affettamento, come in "x.ljust( n)[:n]".

C'è un'altro metodo, zfill(), che aggiunge ad una stringa numerica degli zero a sinistra. Tiene conto dei segni più e meno:

>>> '12'.zfill(5)
'00012'
+>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

L'uso dell'operatore % è il seguente:

>>> import math
>>> print 'Il valore di PI è approssimativamente %5.3f.' % math.pi
Il valore di PI è approssimativamente 3.142.

Se c'è più di un codice di formato nella stringa, si passi una tupla come operando di destra, a esempio:

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

La maggior parte dei codici di formato funziona esattamente come in C e richiede che venga passato il tipo appropriato; comunque, nel caso non lo sia, si ottiene un'eccezione, non un core dump. Il codice di formato %s ha un comportamento più rilassato: se l'argomento corrispondente non è un oggetto stringa, viene convertito in stringa tramite la funzione built-in str(). L'uso di * per passare l'ampiezza o la precisione come argomento separato (numero intero) è supportato, mentre i codici di formato C %n e %p non sono supportati.

Nel caso si abbia una stringa di formato davvero lunga che non si vuole suddividere in parti, sarebbe carino poter fare riferimento alle variabili da formattare tramite il nome piuttosto che tramite la posizione. Ciò può essere ottenuto usando la forma %(nome)formato, p.e.

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

È particolarmente utile in combinazione con la nuova funzione built-in vars(), che restituisce un dizionario contenente tutte le variabili locali.


7.2 Leggere e scrivere file

open() restituisce un oggetto file, ed è perlopiù usata con due argomenti: "open(nomefile, modo)".

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

Il primo argomento è una stringa contenente il nome del file. Il secondo è un'altra stringa contenente pochi caratteri che descrivono il modo nel quale verrà usato il file. modo sarà 'r' (`read') quando il file verrà solamente letto, 'w' per la sola scrittura (`write'), in caso esista già un file con lo stesso nome, esso verrà cancellato, mentre 'a' aprirà il file in ``aggiunta'' (`append'): qualsiasi dato scritto sul file verrà automaticamente aggiunto alla fine dello stesso. 'r+' aprirà il file sia in lettura che in scrittura. L'argomento modo è facoltativo; in caso di omissione verrà assunto essere 'r'.

Su Windows e Macintosh, 'b' aggiunto al modo apre il file in modo binario, per cui ci sono ulteriori modi come 'rb', 'wb' e 'r+b'. Windows distingue tra file di testo e binari; i caratteri EOF dei file di testo vengono leggermente alterati in automatico quando i dati vengono letti o scritti. Questa modifica che avviene di nascosto ai dati dei file è adatta ai file di testo ASCII, ma corromperà i dati binari presenti ad esempio in file JPEG o .EXE. Si raccomanda cautela nell'uso del modo binario quando si sta leggendo o scrivendo su questi tipi di file. Si noti che l'esatta semantica del modo testo su Macintosh dipende dalla libreria C usata.


7.2.1 Metodi degli oggetti file

Nel resto degli esempi di questa sezione si assumerà che sia già stato creato un oggetto file chiamato f.

Per leggere i contenuti di un file, s'invochi f.read(lunghezza), che legge una certa quantità di dati e li restituisce come stringa. lunghezza è un argomento numerico facoltativo. Se omesso o negativo, verrà letto e restituito l'intero contenuto del file. Se il file è troppo grosso rispetto alla memoria della macchina il problema è tutto vostro. Altrimenti viene letto e restituito al più un numero di byte pari a lunghezza. Se è stata raggiunta la fine del file, f.read() restituirà una stringa vuota ("").

>>> f.read()
'Questo è l'intero file.\n'
>>> f.read()
''

f.readline() legge una singola riga dal file; un carattere di fine riga (\n) viene lasciato alla fine della stringa, e viene omesso solo nell'ultima riga del file nel caso non finisca con un fine riga. Ciò rende il valore restituito non ambiguo: se f.readline() restituisce una stringa vuota, è stata raggiunta la fine del file, mentre una riga vuota è rappresentata da '\n', stringa che contiene solo un singolo carattere di fine riga.

>>> f.readline()
'Questa è la prima riga del file.\n'
>>> f.readline()
'Seconda riga del file\n'
>>> f.readline()
''

f.readlines() restituisce una lista contenente tutte le righe di dati presenti nel file. Se le viene passato un parametro facoltativo lunghezza_suggerita, legge tale numero di byte dal file, poi continua fino alla fine della riga e restituisce le righe. Viene spesso usata per consentire la lettura efficiente per righe di un file, senza dover caricare l'intero file in memoria. Verranno restituite solo righe complete.

>>> f.readlines()
['Questa è la prima riga del file.\n', 'Seconda riga del file\n']

f.write(stringa) scrive il contenuto di stringa nel file, restituendo None.

>>> f.write('Questo è un test\n')

f.tell() restituisce un intero che fornisce la posizione nel file dell'oggetto file, misurata in byte dall'inizio del file. Per variare la posizione dell'oggetto file si usi "f.seek(offset, da_cosa)". La posizione viene calcolata aggiungendo ad offset un punto di riferimento, selezionato tramite l'argomento da_cosa. Un valore di da_cosa pari a 0 effettua la misura dall'inizio del file, 1 utilizza come punto di riferimento la posizione attuale, 2 usa la fine del file. da_cosa può essere omesso ed il suo valore predefinito è pari a 0, viene quindi usato come punto di riferimento l'inizio del file.

>>> f=open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5)     # Va al sesto byte nel file
>>> f.read(1)        
'5'
>>> f.seek(-3, 2) # Va al terzo byte prima della fine del file
>>> f.read(1)
'd'

Quando si è terminato di lavorare su un file, si chiami f.close() per chiuderlo e liberare tutte le risorse di sistema occupate dal file aperto. Dopo aver invocato f.close(), i tentativi di usare l'oggetto file falliranno automaticamente.

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

Gli oggetti file hanno alcuni metodi addizionali, come isatty() e truncate() che sono usati meno di frequente; si consulti la Libreria di riferimento per una guida completa agli oggetti file.


7.2.2 Il modulo pickle

Le stringhe possono essere scritte e lette da un file con facilità. I numeri richiedono uno sforzo un po' maggiore, in quanto il metodo read() restituisce solo stringhe, che dovranno essere passate a una funzione tipo int(), che prende una stringa come '123' e restituisce il corrispondente valore numerico 123. Comunque quando si desidera salvare tipi di dati più complessi, quali liste, dizionari o istanze di classe, le cose si fanno assai più complicate.

Per non costringere gli utenti a scrivere e correggere in continuazione codice per salvare tipi di dati complessi, Python fornisce un modulo standard chiamato pickle. Si tratta di un modulo meraviglioso che può prendere pressoché qualsiasi oggetto Python (persino alcune forme di codice Python!) e convertirlo in una rappresentazione sotto forma di stringa; tale processo è chiamato pickling (NdT: letteralmente "conservazione sotto aceto", in pratica si tratta di serializzazione, attenzione a non confonderla con quella del modulo marshal). La ricostruzione dell'oggetto a partire dalla rappresentazione sotto forma di stringa è chiamata unpickling. Tra la serializzazione e la deserializzazione, la stringa che rappresenta l'oggetto può essere immagazzinata in un file, o come dato, o inviata a una macchina remota tramite una connessione di rete.

Se si ha un oggetto x, e un oggetto file f aperto in scrittura, il modo più semplice di fare la serializzazione dell'oggetto occupa solo una riga di codice:

pickle.dump(x, f)

Per fare la deserializzazione dell'oggetto, se f è un oggetto file aperto in scrittura:

x = pickle.load(f)

Ci sono altre varianti del procedimento, usate quando si esegue la serializzazione di molti oggetti o quando non si vuole scrivere i dati ottenuti in un file; si consulti la documentazione completa di pickle nella libreria di riferimento di Python.

pickle è il modo standard per creare oggetti Python che possono essere immagazzinati e riusati da altri programmi o da future esecuzioni dello stesso programma; il termine tecnico è oggetto persistente. Poiché pickle è così ampiamente usato, molti autori di estensioni Python stanno attenti a garantire che i nuovi tipi di dati, quali le matrici, possano essere sottoposti senza problemi a serializzazione ed deserializzazione.

Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.