Appendice C. Consigli e trucchi
Capitolo 1. Installare Python
Capitolo 2. Conoscere Python
- 2.1. Immergersi
|
Nel Python IDE di Windows, potere lanciare un modulo
con File->Run... (Ctrl-R).
-> (Ctrl-R). L'output viene mostrato nella finestra interattiva.
|
|
Nel Python IDE di Mac OS, potete lanciare un modulo con
-> (Cmd-R), ma c'è prima un'opzione importante che dovete settare.
Aprite il modulo nell'IDE, fate comparire il menu delle opzioni dei
moduli cliccando sul triangolo nero posto nell'angolo in alto a destra
della finestra e siate sicuri che
“” sia settato.
Questa impostazione verrà salvata con il modulo, così avrete bisogno
di impostarla una volta sola per ogni modulo.
|
|
Sui sistemi UNIX-compatibili (incluso Mac OS X),
potete lanciare un modulo direttamente dalla linea di
comando:
python odbchelper.py |
- 2.2. Dichiarare le funzioni
|
Nel Visual Basic, le funzioni (che ritornano un valore) iniziano con
function, e le procedure (che non
ritornano un valore) iniziano con sub.
Non esistono procedure in Python.
Sono tutte funzioni, ritornano un valore,
anche se è None ed iniziano tutte con def.
|
|
In Java, C++, ed in altri linguaggi con tipi di dato
statici, dovete specificare il tipo del valore di ritorno
della funzione e di ogni suo argomento. In Python non si
specifica mai espressamente nessun tipo di dato. A seconda
del valore che assegnerete, Python terrà conto del tipo di
dato internamente.
|
- 2.3. Documentare le funzioni
|
Le triple virgolette sono anche un semplice modo per definire
una stringa con singole e doppie virgolette, come il
qq/.../ del Perl.
|
|
Molti IDE di Python usano la doc string per rendere
disponibile una documentazione context-sensitive, così quando
scrivete il nome di una funzione, la sua doc string appare
come tooltip. Questo può essere incredibilmente utile, ma la
sua qualità si basa unicamente sulla doc string che avete
scritto.
|
- 2.4. Tutto è un oggetto
|
import in Python è come require in Perl. Una volta
che avete importato un modulo Python, accedete alle sue
funzioni con
module.function;
una volta che avete richiesto un modulo Perl, accedete
alle sue funzioni con
modulo::funzione.
|
- 2.5. Indentare il codice
|
Python usa il ritorno a capo per separare le istruzioni
e i due punti e l'indentazione per separare i blocchi di
codice. C++ e Java usano un punto e virgola per
separare le istruzioni e le parentesi graffe per separare
i blocchi di codice.
|
- 2.6. Testare i moduli
|
Come il C, Python usa == per i confronti
e = per gli assegnamenti. Al contrario del
C, Python non supporta gli assegnamenti in una singola
riga, così non c'è modo di assegnare per errore il valore
che pensavate di comparare.
|
|
In MacPython c'è un passaggio ulteriore per far funzionare
il trucco if __name__. Fate apparire le opzioni del modulo
cliccando sul triangolo nero, nell'angolo in altro a destra
della finestra e assicuratevi che
“” sia selezionato.
|
- 2.7. Introduzione ai dizionari
|
Un dizionario in Python è come una hash in Perl. In
Perl, le variabili che memorizzano degli hash cominciano
sempre con il carattere %; in Python,
le variabili possono avere qualsiasi nome, e Python tiene
traccia del loro tipo internamente.
|
|
Un dizionario in Python è come l'istanza di una classe
Hashtable in Java.
|
|
Un dizionario Python è come l'istanza di un oggetto
Scripting.Dictionary in Visual Basic.
|
|
I dizionari non hanno il concetto di ordinamento tra elementi.
È errato dire che gli elementi sono
“disordinati”; sono semplicemente non ordinati.
Si tratta di un'importante distinzione che vi darà noia
quando vorrete accedere agli elementi di un dizionario in un
ordine specifico e ripetibile (ad esempio in ordine
alfabetico di chiave). Ci sono dei modi per farlo,
semplicemente non sono predefiniti nel dizionario.
|
- 2.8. Introduzione alle liste
|
Una lista in Python è come un array in Perl.
In Perl, le variabili che contengono array hanno un nome che
inizia sempre con il carattere @; in
Python le variabili possono avere qualunque nome, è il
linguaggio che tiene traccia internamente del tipo di dato.
|
|
Una lista in Python è molto più di un array in Java
(sebbene possa essere usato allo stesso modo, se davvero
non volete altro). Un'analogia migliore sarebbe la classe
Vector, che può contenere oggetti
di tipo arbitrario e si espande automaticamente quando vi si
aggiungono nuovi elementi.
|
|
Prima della versione 2.2.1, Python non aveva un tipo di dato
booleano distinto dai numeri interi. Per compensare, Python
accettava quasi tutto in un contesto booleano (come un comando
if), secondo la seguente regola: 0 è falso; tutti gli
altri numeri sono veri. Una stringa vuota
("") è falsa; tutte le altre stringhe sono
vere. Una lista vuota ([]) è falsa; tutte
le altre liste sono vere. Una tupla vuota
(()) è falsa; tutte le altre tuple sono
vere. Un dizionario vuoto ({}) è falso;
tutti gli altri dizionari sono veri. Queste regole valgono
ancora in Python 2.2.1 e oltre, ma ora si può usare anche un
valore booleano vero e proprio, che ha valore
True o False.
Notate le maiuscole; questi valori, come tutto il resto in
Python, sono case-sensitive.
|
- 2.9. Introduzione alle tuple
|
Le tuple possono essere convertite in liste e viceversa.
La funzione built-in tuple prende una lista e ritorna
una tupla con gli stessi elementi, mentre la funzione
list prende una tupla e ritorna una lista. In effetti,
tuple congela una lista e list scongela una tupla.
|
- 2.10. Definire le variabili
|
Quando un comando è diviso su più linee attraverso il
continuatore di linea
(“\”), la linea seguente
può essere indentata in ogni maniera; Le rigide regole di
indentazione di Python non vengono applicate.
Se il vostro Python IDE
auto-indenta la linea continuata, probabilmente dovreste
accettare il suo default a meno che non abbiate ottime
ragioni per non farlo.
|
|
A rigor di termini, espressioni fra parentesi, parentesi
quadre o graffe (come per la
definizione
di un dizionario),
possono essere divise in linee multiple con o senza il
carattere di continuazione
linea(“\”).
Io preferisco includere il backslash anche quando non è
richiesto perché credo che renda il codice più leggibile,
ma è solo una questione di stile.
|
- 2.12. Formattare le stringhe
|
La formattazione delle stringhe in Python utilizza la stessa
sintassi della funzione C sprintf.
|
- 2.14. Concatenare liste e suddividere stringhe
|
join funziona solamente su liste di stringhe, non applica
alcuna forzatura sul tipo. Concatenare una lista che contiene
uno o più oggetti che non sono di tipo stringa genererà
un'eccezione.
|
|
anystring.split
(separatore, 1)
è un'utile tecnica quando volete cercare in una stringa la
presenza di una particolare sottostringa e quindi utilizzare
tutto ciò che c'è prima di detta sottostringa (che va a
finire nel primo elemento della lista ritornata) e tutto
quello che c'è dopo (che va a finire nel secondo elemento).
|
Capitolo 3. La potenza dell'introspezione
- 3.2. Argomenti opzionali ed argomenti con nome
|
L'unica cosa che dovete fare per chiamare una funzione è
specificare un valore (in qualche modo) per ogni argomento
richiesto; il modo e l'ordine in cui lo fate è a vostra
discrezione.
|
- 3.3. type, str, dir, ed altre funzioni built-in
|
Python è dotato di un eccellente manuale di riferimento,
che dovreste usare spesso per conoscere tutti i moduli che
Python ha da offrire. Ma mentre nella maggior parte dei
linguaggi vi ritroverete a dover tornare spesso sul manuale
(o pagine man o ... Dio vi aiuti, MSDN)
per ricordare come si usano questi moduli, Python è
largamente auto-documentante.
|
- 3.6. Le particolarità degli
operatori and e or
|
Il trucchetto and-or, cioè
bool and
a or b,
non funziona come il costrutto C bool ? a : b quando
a ha un valore falso in un contesto booleano.
|
- 3.7. Usare le funzioni lambda
|
l'utilizzo o meno delle funzioni lambda è questione
di stile. Usarle non è mai necessario; in ogni caso in cui
vengono usate è possibile definire una funzione normale e
usarla al posto della funzione lambda.
Le funzioni lambda sono comode, ad esempio, nei
casi in cui si voglia incapsulare codice specifico, non
riutilizzato, senza riempire il programma di piccolissime
funzioni.
|
- 3.8. Unire il tutto
|
In SQL, dovete usare IS NULL invece di
= NULL per confrontare un valore nullo.
In Python, potete usare indifferentemente
== None o is None, ma
is None è più efficiente.
|
Capitolo 4. Una struttura orientata agli oggetti
- 4.2. Importare i moduli usando from module import
|
from module import * in Python è simile allo
use module
in Perl; import module nel Python è come il
require module
del Perl.
|
|
from module import * in Python è come import
module.* in Java;
import module in Python è simile a import
module del Java.
|
- 4.3. Definire classi
|
Lo statement pass in Python è come un paio di graffe vuote
({}) in Java od in C.
|
|
In Python, l'antenato di una classe è semplicemente elencato
tra parentesi subito dopo il nome della classe. Non c'è alcuna
keyword come la extends di Java.
|
|
Anche se non la discuterò in profondità nel libro, Python
supporta l'ereditarietà multipla. Nelle parentesi che seguono
il nome della classe, potete elencare quanti antenati volete,
separati da virgole.
|
|
Per convenzione, il primo argomento di ogni metodo di una classe (il
riferimento all'istanza corrente) viene chiamato self. Questo argomento
ricopre il ruolo della parola riservata this in C++ o Java, ma self
non è una parola riservata in Python, è semplicemente una convenzione sui
nomi. Non di meno, vi prego di non chiamarlo diversamente da self; è una
convenzione molto forte.
|
|
Quando definite i vostri metodi nella classe,
dovete elencare esplicitamente self
come il primo argomento di ogni metodo, incluso __init__.
Quando chiamate il metodo di una classe antenata dall'interno
della vostra classe, dovete includere
l'argomento self. Invece, quando chiamate i metodi della vostra
classe dall'esterno, non dovete specificare affatto
l'argomento self, lo saltate per intero e Python
automaticamente aggiunge il riferimento all'istanza per voi.
Temo che inizialmente possa confondere; non è proprio
inconsistente ma può sembrarlo perché fa
affidamento su una distinzione (tra metodi bound ed unbound)
che ancora non conoscete.
|
|
i metodi __init__ sono opzionali, ma quando ne definite uno, dovete
ricordarvi di chiamare esplicitamente il metodo __init__
dell'antenato. Questo è generalmente vero; quando un
discendente vuole estendere il comportamento di un antenato,
il metodo del discendente deve esplicitamente chiamare il
metodo dell'antenato nel momento opportuno e con gli
opportuni argomenti.
|
- 4.4. Istanziare classi
|
In Python, semplicemente chiamate una classe come se fosse
una funzione per creare una sua nuova istanza. Non c'è
alcun operatore esplicito new come in C++ o in Java.
|
- 4.5. UserDict: una classe wrapper
|
Nella IDE di Python su Windows, potete aprire
rapidamente qualunque modulo nel vostro library path usando
-> (Ctrl-L).
|
|
Java e Powerbuilder supportano l'overload di funzioni per
lista di argomenti, cioè una classe può avere più metodi con
lo stesso nome, ma diverso numero di argomenti o argomenti di
tipo diverso. Altri linguaggi (principalmente PL/SQL)
supportano l'overload di funzioni per nome di argomento; cioè
una classe può avere più metodi con lo stesso nome e lo
stesso numero di argomenti dello stesso tipo, ma i nomi degli
argomenti sono diversi. Python non supporta nessuno di
questi; non ha nessuna forma di overload di funzione. I metodi
sono definiti solamente dal loro nome e ci può essere
solamente un metodo per ogni classe con un dato nome. Così, se
una classe discendente ha un metodo __init__, questo sovrascrive
sempre il metodo __init__ dell'antenato,
anche se il discendente lo definisce con una lista di
argomenti diversa. La stessa regola si applica ad ogni altro
metodo.
|
|
Guido, l'autore originale di Python, spiega l'override dei
metodi in questo modo: "Classi derivate possono sovrascrivere
i metodi delle loro classi base. Siccome i metodi non hanno
privilegi speciali quando chiamano altri metodi dello stesso
oggetto, un metodo di una classe base che chiama un altro
metodo definito nella stessa classe base, può infatti finire
per chiamare un metodo di una classe derivata che lo
sovrascrive. (Per i programmatori C++ tutti i metodi in
Python sono effettivamente virtual.)" Se questo per voi non
ha senso (confonde un sacco pure me), sentitevi liberi di
ignorarlo. Penso che lo capirò più avanti.
|
|
Assegnate sempre un valore iniziale a tutti gli attributi
di un'istanza nel metodo __init__. Vi risparmierà ore di
debugging più tardi, spese a tracciare tutte le eccezioni
AttributeError che genera il
programma perché state referenziando degli attributi non
inizializzati (e quindi inesistenti).
|
- 4.6. Metodi speciali per le classi
|
Quando accedete agli attributi di una classe, avete bisogno
di qualificare il nome dell'attributo:
self.attributo.
Quando chiamate altri metodi di una classe, dovete
qualificare il nome del metodo:
self.metodo.
|
- 4.7. Metodi speciali di classe avanzati
|
In Java, determinate quando due variabili di tipo stringa
referenziano la stessa memoria fisica utilizzando
str1 == str2. Questa è chiamata
identità di oggetto, ed è espressa in
Python come str1 is str2. Per confrontare
valori di tipo stringa in Java, dovreste usare
str1.equals(str2); in Python, usereste
str1 == str2. Programmatori Java a cui è
stato insegnato a credere che il mondo è un posto migliore in
quanto == in Java confronta l'identità
invece del valore potrebbero avere qualche difficoltà
nell'acquisire questo nuovo “concetto” di
Python.
|
|
Mentre altri linguaggi orientati agli oggetti vi lasciano
definire il modello fisico di un oggetto (“questo
oggetto ha il metodo GetLength”),
i metodi speciali di classe Python come __len__ vi
permettono di definire il modello logico di un oggetto
(“questo oggetto ha una lunghezza”).
|
- 4.8. Attributi di classe
|
In Java, sia le variabili statiche (chiamate attributi di
classe in Python) che le variabili di istanza (chiamate
attributi dato in Python), sono definite immediatamente dopo
la definizione della classe (il primo con la parola chiave
static, il secondo senza). In Python,
solo gli attributi di classe posso essere definiti così; gli
attributi dato sono definiti nel metodo __init__.
|
- 4.9. Funzioni private
|
Se il nome di una funzione, metodo di classe o attributo in
Python inizia con (ma non finisce con) due underscore, è
privato; ogni altra cosa è pubblica.
|
|
In Python, tutti i metodi speciali (come
__setitem__)
e gli attributi built-in (come
__doc__)
seguono una convenzione standard sui nomi: iniziano e
finiscono con due underscore. Non nominate i vostri metodi e
attributi in questa maniera, servirà solo a confondervi.
Inoltre, in futuro, potrebbe anche confondere altri che
leggeranno il vostro codice.
|
|
Python non ha il concetto di metodi protetti di classe
(accessibili solo nella loro stessa classe ed in quelle
discendenti). I metodi di classe sono o privati (accessibili
unicamente nella loro stessa classe) o pubblici (accessibili
ovunque).
|
- 4.10. Gestire le eccezioni
|
Python usa try...except per gestire le eccezioni e
raise per generarle. Java e C++ usano
try...catch per gestirle e
throw per generarle.
|
- 4.14. Il modulo os
|
Ogniqualvolta sia possibile, dovreste usare le funzioni in
os e os.path per file, directory e manipolazione dei
percorsi. Questi moduli sono dei wrapper ai moduli specifici
per piattaforma, per cui funzioni come
os.path.split funzionano su UNIX,
Windows, Mac OS ed ogni altra possibile piattaforma
supportata da Python.
|
Capitolo 5. Elaborare HTML
- 5.2. Introduciamo sgmllib.py
|
Python 2.0 ha un bug per il quale SGMLParser non riconosce del tutto le
dichiarazioni (handle_decl non viene mai chiamato), questo implica che i
DOCTYPE vengano ignorati senza che sia segnalato. Questo è stato corretto
in Python 2.1.
|
|
Nella IDE di Python su Windows, potete specificare
argomenti da linea di comando nella finestra di dialogo
“Run script”. Argomenti multipli vanno separati
da spazi.
|
- 5.4. Introdurre BaseHTMLProcessor.py
|
La specifica HTML richiede che tutto ciò che non è HTML
(come JavaScript lato client) debba essere racchiuso tra
commenti HTML, ma non in tutte le pagine ciò è stato fatto
(e tutti i moderni browsers chiudono un occhio in merito).
BaseHTMLProcessor non perdona; se uno script è inserito
in modo improprio, verrà analizzato come se fosse codice HTML.
Per esempio, se lo script contiene i simboli di minore e maggiore,
SGMLParser potrebbe pensare, sbagliando, di aver trovato tag e
attributi. SGMLParser converte sempre i tag e i nomi degli
attributi in lettere minuscole, il che potrebbe bloccare lo
script, e BaseHTMLProcessor racchiude sempre i valori tra
doppi apici (anche se originariamente il documento HTML usava
apici singoli o niente del tutto), cosa che di sicuro
interromperebbe lo script. Proteggete sempre i vostri script
lato client inserendoli dentro commenti HTML.
|
- 5.5. locals e globals
|
Python 2.2 introdusse un subdolo ma importante cambiamento che modificò
l'ordine di ricerca dei namespace: gli scope nidificati. Nelle versioni di
Python precedenti al 2.2, quando vi riferite ad una variabile dentro ad una
funzione nidificata o una
funzione lambda, Python
ricercherà quella variabile
nel namespace corrente della funzione, e poi nel namespace del modulo.
Python 2.2 ricercherà la variabile nel namespace della funzione corrente,
poi nel namespace della funzione genitore, e poi nel
namespace del modulo.
Python 2.1 può lavorare in ogni modo;
predefinitamente, lavora come Python 2.0, ma potete
aggiugere la seguente linea di codice in cima al vostro modulo
per farlo funzionare come Python 2.2:
from __future__ import nested_scopes |
|
Usando le funzioni locals e globals, potete ottenere
il valore di variabili arbitrarie dinamicamente, rendendo
disponibile il nome della variabile come una stringa.
Questo rispecchia la funzionalità della funzione
getattr, che vi
permette di accedere a funzioni arbitrarie dinamicamente,
rendendo disponibile il nome della funzione come stringa.
|
- 5.6. Formattazione di stringhe basata su dizionario
|
Usare una formattazione di stringhe basata su dizionari con locals è
un modo conveniente per ottenere una formattazione di stringhe complessa
in maniera più leggibile, ma ha un prezzo. C'è una minima perdita di
performance nell'effettuare una chiamata a locals, in quanto
locals costruisce una
copia dello spazio dei nomi locale.
|
Capitolo 6. Elaborare XML
- 6.2. Package
|
Un package è una directory con il file speciale
__init__.py dentro. Il file __init__.py
definisce gli attributi ed i metodi del package.
Non deve obbligatoriamente
definire nulla; può essere un file vuoto,
ma deve esistere. Ma se __init__.py non esiste,
la directory è soltanto una directory, non un
package e non può essere importata, contenere
moduli o package annidati.
|
- 6.6. Accedere agli attributi di un elemento
|
Questa sezione potrebbe risultare un po' confusa a causa
della sovrapposizione nella terminologia. Gli elementi in
un documento XML hanno degli attributi ed anche gli
oggetti Python hanno degli attributi. Quando analizziamo
un documento XML, otteniamo un gruppo di oggetti
Python che rappresentano tutti i pezzi del documento
XML ed alcuni di questi oggetti rappresentano gli
attributi degli elementi XML. Ma anche gli oggetti
(Python) che rappresentano gli attributi (XML) hanno
attributi, che vengono utilizzati per accedere alle varie
parti degli attributi (XML) che l'oggetto rappresenta.
Ve l'ho detto che vi avrebbe confuso. Sono aperto a
suggerimenti su come distinguere le due cose in maniera
più chiara.
|
|
Come un dizionario, gli attributi di un elemento XML
non hanno ordinamento. Gli attributi possono
essere elencati in un certo ordine nel
documento XML originale e gli oggetti
Attr possono essere elencati
in un certo
ordine quando il documento XML viene interpretato
in oggetti Python, ma questi ordinamenti sono
arbitrari e non dovrebbero avere alcun significato
particolare. Dovreste sempre accedere agli attributi
in base al nome, come nel caso delle chiavi di un dizionario.
|
Capitolo 7. Test delle unità di codice
- 7.2. Introduzione al modulo romantest.py
|
unittest è incluso con Python 2.1 e successivi.
Gli utilizzatori di Python 2.0 possono scaricarlo da
pyunit.sourceforge.net.
|
- 7.8. roman.py, fase 3
|
La cosa più importante che test delle unità di codice, condotti
in modo esaustivo, possano comunicarci è il momento in cui si
deve smettere di scrivere un programma.
Quando tutti i test di un modulo hanno successo, è il momento
di smettere di scrivere quel modulo.
|
- 7.10. roman.py, fase 5
|
Quando tutti i test passano con successo,
smettete di scrivere codice.
|
- 7.13. Rifattorizzazione
|
Ogni qualvolta si ha intenzione di usare più di una volta
una espressione regolare, la si dovrebbe prima compilare
per ottenerne il corrispondente oggetto pattern, e quindi
chiamare direttamente i metodi di tale oggetto.
|
Capitolo 8. Programmazione orientata ai dati