Scriviamo in IDLE un semplice ciclo che stampa i quadrati e i cubi dei numeri da 1 a 10 (dopo aver scritto la seconda riga dovete premere due volte <INVIO> per eseguirlo):
>>> for i in range(1, 11): print(i, i ** 2, i ** 3)
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
L'output di IDLE è abbastanza sgraziato: man mano che i numeri diventano più grandi occupano sempre più spazi, e così non sono incolonnati. Si potrebbe senz'altro cercare di allineare questi numeri con delle istruzioni supplementari, ma ciò ci porterebbe a parecchie complicazioni.
Ecco un altro esempio: qui abbiamo una lista di venditori, ognuno dei quali ha ottenuto un certo numero di vendite e ha diritto ad una provvigione (un venditore è memorizzato in un vocabolario con nome, cognome, vendite e percentuale della provvigione). Vogliamo stampare un elenco dei venditori con l'ammontare della provvigione spettante ad ognuno di essi. Copiate nell'editor di testo questo programma:
venditori = [
{ "cognome":"Abbotti", "nome":"Giovanni", "vendite":36515.35, "provvigione":2.35 },
{ "cognome":"Bellini", "nome":"Luigi", "vendite":45210.00, "provvigione":2.5 },
{ "cognome":"Cardellini", "nome":"Matteo", "vendite":28534.50, "provvigione":3.12 },
{ "cognome":"Decini", "nome":"Piervittorio", "vendite":33782.80, "provvigione":2.94 }
]
for v in venditori:
print(v["cognome"], v["nome"], v["vendite"] * v["provvigione"] / 100)
Se lo eseguite otterrete questo output:
Abbotti Giovanni 858.110725
Bellini Luigi 1130.25
Cardellini Matteo 890.2764
Decini Piervittorio 993.21432
Anche qui l'effetto grafico è orribile: nomi, cognomi e numeri dovrebbero essere stampati su tre colonne diverse; i numeri, poi, dovrebbero essere allineati alla virgola e tutti con due decimali. Anche questo si potrebbe fare con delle istruzioni supplementari, ma alla fine impiegheremmo probabilmente più tempo per ottenere un output corretto che per risolvere il problema di calcolare le provvigioni!
Tutti i linguaggi di programmazione (alcuni dei quali risalgono ai tempi in cui l'unico output possibile era la
stampa su carta) offrono delle istruzioni per controllare approfonditamente quello che si stampa (allineamento di numeri e parole,
numero di decimali, ecc.). La comodità della funzione print()
di Python fa sì che, il più delle volte, sia sufficiente
usarla nella sua forma più semplice, ma se vogliamo un maggiore controllo anche Python ci offre parecchi strumenti.
Va detto che nel corso degli anni e delle varie versioni Python ha avuto un evoluzione: fino a Python 2 veniva adottata una serie di istruzioni che richiamavano quelle usate nel linguaggio C, dalla versione 3 sono state invece introdotte delle nuove istruzioni totalmente differenti, che danno maggiori possibilità di controllo. Oggi ci si riferisce ai due sistemi come "Old style formatting" e "New style formatting". L'old style è ancora accettato da Python 3 ma i nuovi utenti sono incoraggiati ad usare il new style. Per questa ragione parleremo qui solo di quest'ultimo, ed anche così dovremo limitarci solo alle caratteristiche più usate, perchè le varie combinazioni di possibilità sono veramente moltissime e costituiscono un vero e proprio "mini linguaggio" all'interno di Python. Del resto, chi avesse già dimestichezza con il C non dovrebbe avere troppi problemi ad imparare le istruzioni old style.
La base della formattazione "new style" in Python è il metodo format()
, che si può
applicare ad una stringa con la solita sintassi con il punto. Apriamo IDLE e scriviamo:
>>> "Ciao mamma!".format()
'Ciao mamma!'
>>> s = "Ciao Luigi!" >>> s.format()
'Ciao Luigi!'
Notate che abbiamo applicato il metodo prima ad una costante (cioè una stringa scritta esplicitamente tra virgolette) e
poi ad una variabile contenente una stringa. IDLE come al solito risponde stampando il valore restituito dal metodo, cioè, per il
momento, la stringa immutata. Vedremo adesso come manipolare per mezzo del format()
quello che vogliamo stampare.
Il format()
interviene sulla stringa alla quale è applicato modificandola in vari modi. Per
consentirgli di fare ciò dobbiamo per prima cosa indicargli le parti della stringa che deve modificare tramite dei
campi di sostituzione (nella documentazione ufficiale replacement fields, nel seguito
li chiameremo solo campi), seguendo queste regole:
format()
, che indica il testo da
sostituire al posto del campo corrispondenteEcco subito un esempio per capire meglio:
>>> s = "Ciao {}!" # stringa che contiene un campo di sostituzione >>> s.format("Luigi") # sostituisce "Luigi" al posto del campo
'Ciao Luigi!'
>>> s.format("Sandra") # sostituisce "Sandra"
'Ciao Sandra'
>>> a = 10 >>> b = 20 >>> "{} x {} = {}".format(a, b, a * b) # tre campi, tre parametri
'10 x 20 = 200'
>>> "{{{}}}".format(a) # come scrivere le parentesi graffe
'{10}'
Nella prima istruzione abbiamo definito una stringa s
contenente un campo di sostituzione (le due parentesi
graffe). Nella seconda e terza abbiamo usato il format()
per sostituire al posto delle parentesi prima
"Luigi"
e poi "Sandra"
. Osserviamo ora le altre istruzioni: prima assegniamo un valore a due variabili
a
e b
, poi le stampiamo (con il loro prodotto) usando un format()
. Infine, nell'ultima
istruzione, stampiamo una variabile tra parentesi graffe: dobbiamo usarne tre di seguito (due per la parentesi ed una per il
campo).
Scrivendo le parentesi aperte e subito chiuse i parametri del format()
verranno assegnati ai campi
secondo il loro ordine, ma possiamo anche aggirare questa regola indicando noi la corrispondenza tra campi e parametri. Anche qui
abbiamo diverse opzioni (ve l'ho detto, si tratta di un vero e proprio mini-linguaggio, se vi sembra troppo complicato potete
anche saltare questa parte e riprenderla quando avrete le idee più chiare).
>>> "{0} e {1} sono miei amici".format("Luigi", "Sandra")
'Luigi e Sandra sono miei amici'
>>> "{1} e {0} sono miei amici".format("Luigi", "Sandra")
'Sandra e Luigi sono miei amici'
>>> "{0} {1} {0} {0} {1}".format("Luigi", "Sandra")
'Luigi Sandra Luigi Luigi Sandra'
format()
come
keyword argument (se non ricordate cosa sono guardate
qui. Attenzione! I nomi dati tra parentesi
graffe valgono solo all'interno della coppia stringa-format e non possiamo usare variabili che abbiamo definito
esternamente.
il secondo tentativo provoca un errore, perchè il nome>>> "Voglio bene a {amico}".format(amico="Luigi")
'Voglio bene a Luigi'
>>> amico = "Sandra" >>> "Voglio bene a {amico}".format() # questo NON si puo' fare
Traceback (most recent call last): File "
", line 1, in "Voglio bene a {amico}".format() KeyError: 'amico'
amico
deve essere definito all'interno del
format()
.
A questo punto, probabilmente, molti di voi staranno pensando: ma fino ad ora non c'è niente di nuovo! Abbiamo solo imparato
un modo più complicato di fare cose che già sapevamo fare! E' vero, ma da ora cominciamo a vedere altre regole che ci permettono
di modficare più radicalmente la stringa a cui è applicato il format()
. All'interno di un campo di
sostituzione possiamo inserire altri caratteri specificatori di formato che controllano la larghezza della
stampa, l'allineamento ecc.
Cominciamo dalla formattazione del testo: gli specificatori di formato devono cominciare con un carattere due punti
':'
e devono seguire questa sintassi (un nome racchiuso tra parentesi quadre indica che esso è opzionale):
format()
usa il normale spazio vuoto.Carattere | Significato |
---|---|
< |
Testo allineato a sinistra (questo è il comportamento di default se il carattere di allineamento viene omesso) |
^ |
Testo centrato |
> |
Testo allineato a destra |
larghezza
il
comportamento di default è quello di non troncare il testo, superando così il numero di caratteri
indicato. Se invece vogliamo il troncamento possiamo indicarlo ponendo dopo la larghezza un punto ed un altro numero, che indica
il massimo numero di caratteri (a partire da sinistra) da stampare, dopodichè il testo viene troncato.Cerchiamo subito di capire meglio con qualche esempio:
>>> "{:>20}".format("Luigi") # larghezza 20, allineato a destra
' Luigi'
>>> "{:<20}".format("Luigi") # larghezza 20, allineato a sinistra
'Luigi '
>>> "{:20}".format("Luigi") # il testo e' allineato a sinistra di default
'Luigi '
>>> "{:_^20}".format("Luigi") # centrato e con carattere di riempimento
'_______Luigi________'
>>> "{:20}".format("Precipitevolissimevolmente") # stringa lunga senza troncamento
'Precipitevolissimevolmente'
>>> "{:20.20}".format("Precipitevolissimevolmente") # stringa lunga con troncamento
'Precipitevolissimevo'
>>> # keyword arguments (vanno PRIMA dei due punti) >>> "{nome:>15}{cognome:>15}".format(nome="Luigi", cognome="Rossi")
' Luigi Rossi'
Ricordate di seguire esattamente l'ordine indicato, altrimenti otterrete un errore:
>>> "{:10>}".format("Luigi") # il > va PRIMA del 10
Traceback (most recent call last): File "
", line 1, in "{:10>}".format("Luigi") ValueError: Unknown format code '>' for object of type 'str'
Queste regole ci permettono già di migliorare notevolmente il nostro programma venditori.py. Modificate le ultime righe in questo modo:
. . .
for v in venditori:
s = "{:15} {:15} {}".format(v["cognome"], v["nome"], v["vendite"] * \
v["provvigione"] / 100)
print(s)
che ci faranno ottenere finalmente i nomi dei venditori incolonnati:
Abbotti Giovanni 858.110725
Bellini Luigi 1130.25
Cardellini Matteo 890.2764
Decini Piervittorio 993.21432
s
: allineate i nomi a destra o al centro, usate
l'asterisco "*" come carattere di riempimento, variate la larghezza dei campi
Abbotti G. 858.110725
Bellini L. 1130.25
Cardellini M. 890.2764
Decini P. 993.21432
SUGGERIMENTO: dovete stampare solo la prima lettera del nome ... il punto dopo l'iniziale va invece inserito nella stringa
a cui viene applicato il format, dopo il campo di sostituzione. Lasciate tre spazi tra l'iniziale e l'importo delle
vendite
Per la formattazione dei numeri le cose si complicano un po', in quanto Python ci mette a disposizione moltissime opzioni: possiamo controllare il numero di decimali, la base (decimale, binaria, esadecimale ...), la notazione esponenziale ...; nel seguito cercherò di essere il più completo possibile, ma va da sè che probabilmente non avrete mai bisogno di tutte queste opzioni, e quindi vi basta capire gli esempi principali; se in futuro avrete bisogno di stampare numeri in forma esadecimale vi basterà cercare il codice specifico in qualche reference. Ecco il formato completo degli specificatori:
Di nuovo, lo specificatore deve cominciare con i due punti, seguiti (nell'ordine preciso in cui sono indicati) dai vari blocchi, tutti opzionali. Analizziamoli uno per uno (per maggiore chiarezza non li elencherò nello stesso ordine in cui si presentano):
Carattere | Significato | Esempio |
---|---|---|
< |
Numero allineato a sinistra | >>> "{:<15}".format(-10)
|
^ |
Numero centrato | >>> "{:^15}".format(-10)
|
> |
Numero allineato a destra (è il default per i numeri, notate che il testo viene invece allineato di default a sinistra) | >>> "{:>15}".format(-10)
>>> "{:15}".format(-10) # lo stesso
|
= |
Il carattere di riempimento viene posto tra il segno ed il numero. | >>> "{:=15}".format(-10)
|
Carattere | Significato | Esempio |
---|---|---|
+ |
Il segno viene sempre anteposto, sia ai numeri positivi che a quelli negativi | >>> "{:+}".format(10) '+10' >>> "{:+}".format(-10) '-10' |
- |
Il segno viene anteposto solo ai numeri negativi. Questo è il comportamento di default se il carattere viene omesso | >>> "{:-}".format(10) '10' >>> "{:-}".format(-10) '-10' |
spazio | Viene anteposto un segno meno per i numeri negativi ed uno spazio per i numeri positivi | >>> "{: }".format(10) ' 10' >>> "{: }".format(-10) '-10' |
riempimento.allineamento
nel
caso più comune di riempimento con zeri. Se è presente viene usato lo 0 come carattere di riempimento e l'eventuale segno
è anteposto agli zeri, senza bisogno di usare il carattere "=" già visto sopra.
>>> "{:10}".format("-123") # senza zeri iniziali
' -123'
>>> "{:010}".format("-123") # con gli zeri iniziali
'-000000123'
>>> "{:,}".format(1234567.89)
'1,234,567.89'
.precisione
(che
provoca ugualmente un errore):
Carattere | Significato | Esempio |
---|---|---|
b |
Stampa il numero in formato binario (cioè in base 2) | >>> "{:b}".format(125) '1111101' |
c |
Stampa il carattere unicode corrispondente al numero | >>> "{:c}".format(125) '}' |
d oppure n | Stampa il numero in formato decimale. Questo è il formato di default se il il carattere viene omesso ed il numero da stampare è un intero. | >>> "{:d}".format(125) '125' |
o |
Stampa il numero in formato ottale (cioè in base 8) | >>> "{:o}".format(17) '21' |
x |
Stampa il numero in formato esadecimale (cioè in base 16); per le cifre da 10 a 15 usa le lettere minuscole a ... f. | >>> "{:x}".format(16253) '3f7d' |
X |
Stampa il numero in formato esadecimale (cioè in base 16); per le cifre da 10 a 15 usa le lettere maiuscole A ... F. | >>> "{:X}".format(16253) '3F7D' |
.precisione
è opzionale (se omesso le cifre decimali sono 6 di deafult):
Carattere | Significato | Esempio |
---|---|---|
e |
Stampa il numero in notazione scientifica esponenziale (una parte intera
tra 1 e 9, seguita dai decimali, seguita dalla 'e' e dalla potenza di 10). Il numero delle cifre decimali
corrisponde a quello indicato nel blocco .precisione : se il numero ha meno cifre decimali aggiunge
degli zeri, se ne ha di più lo arrotonda |
>>> "{:e}".format(12.3) '1.230000e+01' >>> "{:.2e}".format(123456) '1.23e+05' |
E |
Come il precedente, ma usa una E maiuscola per indicare l'esponente |
>>> "{:.4e}".format(12.3) '1.2300E+01' |
f oppure F | Stampa il numero con un numero fisso di cifre decimali (indicato dal blocco .precisione ).
Se il numero ha meno cifre decimali aggiunge degli zeri, se ne ha di più lo arrotonda |
>>> "{:f}".format(12.3) '12.300000' >>> "{:.2f}".format(12.3678) '12.37' |
g oppure G |
Indica il formato generale: fa scegliere autonomamente a Python se stampare il numero in
notazione decimale o esponenziale e quante cifre decimali indicare, in base al contenuto dei blocchi
larghezza e .precisione . Per questo formato il blocco .precisione non rappresenta
il numero delle cifre decimali, ma il numero massimo delle cifre (diverse dagli zeri iniziali o finali) da stampare.
Questo è il formato di default se il carattere non viene indicato ed il numero è un float . |
>>> >>> "{:.8g}".format(123.4) '123.4' >>> >>> "{:.3g}".format(123.4) '123' >>> >>> "{:.2g}".format(123.4) '1.2e+02' |
% | Stampa il numero come percentuale, cioè moltiplicato per 100 e seguito dal carattere '%' | >>> "{:.2%}".format(0.123) '12.30%' |
Finito lungo questo elenco di opzioni potete provare a fare qualche esercizio:
25
)>>> "{:}".format(25)
Ora cambiate lo specificatore tra parentesi graffe, in modo da ottenere questi output (ricordate sempre, se dovete applicare
più modificatori, di seguire esattamente l'ordine del modello dato, o otterrete degli errori):
'0025'
'+0025'
'**+25'
'+**25'
'25.00'
'+2.50e01'
'+ 2.50E01' (uno spazio prima del numero)
' 11001' (tre spazi prima del numero)
'11001 ' (tre spazi dopo)
'0x19'
ESERCIZIO 4.3 Riprendete il nostro esempio iniziale:
>>> for i in range(1, 11):
print(i, i ** 2, i ** 3)
e cambiate l'istruzione print()
in modo da avere questo output:
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
SUGGERIMENTO: dovete sostituire i tre argomenti della print()
con un solo argomento: una stringa che contiene
tre campi di sostituzione formattati con il format()
. Notate l'ultima riga: c'è un solo spazio tra 10 100 1000
Abbotti Giovanni 858.11
Bellini Luigi 1130.25
Cardellini Matteo 890.28
Decini Piervittorio 993.21
Fine della lezione