Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Redazione HTML.it
FONDAMENTI DI PROGRAMMAZIONE
IN
1
2
Lezione 1di 40
Introduzione e un po’ di storia
Python è un linguaggio di programmazione moderno, dalla sintassi
semplice e potente che ne facilita l’apprendimento. Gli ambiti di
applicazione di questo linguaggio di programmazione sono svariati:
sviluppo di siti o applicazioni Web e desktop, realizzazione di
interfacce grafiche, amministrazione di sistema, calcolo
scientifico e numerico, database, giochi, grafica 3D, eccetera.
L’obiettivo di questa guida è quello di fornire una panoramica
generale sulla programmazione in Python, che permetta al lettore
di sviluppare in modo facile e veloce le proprie applicazioni.
Nota: Questa è la riedizione della guida Python di Stefano Riccio,
revisionata e aggiornata da Ezio Melotti, Andrea Sindoni, e dalla
redazione di HTML.it
Prima di iniziare, può essere utile capire come e dove è nato
Python. Per farlo bisogna tornare un po’ indietro nel tempo
precisamente nei primi anni ottanta. In quegli anni, al National
Research Institute for Mathematics and Computer Science (CWI) di
Amsterdam, alcuni ricercatori tra cui Guido Van Rossum hanno
sviluppato un linguaggio di nome ABC, molto potente ed elegante,
che era diventato popolare nel mondo Unix.
Guido Van Rossum
Qualche anno dopo (fine anni ottanta) Guido Van Rossum ha avuto
una serie di idee mirate al miglioramento di ABC, e pertanto si
mise a lavorare allo sviluppo di un nuovo linguaggio: Python,
appunto.
Nel 1996 scrisse come prefazione del libro “Programming Python”,
prima edizione, le seguenti parole:
«Più di sei anni fa, nel dicembre 1989, stavo cercando un progetto
di programmazione per “hobby” che mi avrebbe dovuto tenere
occupato nella settimana vicina a Natale. Il mio ufficio… sarebbe
stato chiuso, ma io avevo un computer, e non molto di più. Decisi
di scrivere un interprete per un nuovo linguaggio di scripting a
cui avrei pensato dopo: un discendente dell’ABC, che sarebbe
dovuto appartenere agli hacker di Unix. Scelsi Python come nome
3
per il progetto, essendo leggermente irriverente (e perchè sono un
grande fan di Monty Python’s Flying Circus).»
Nel 2000 Van Rossum e il suo team si trasferiscono presso
BeOpen.com e formano i BeOpen PythonLabs team, con Python giunto
alla versione 1.6. Poco tempo dopo viene rilasciata la versione
2.0, che, tra le altre cose, migliorava il linguaggio con
l’aggiunta delle “list comprehension“.
Nel 2001 viene rilasciato Python 2.1, e ridefinita la licenza come
“Python Software Foundation License”. Python 2.2 fu considerato un
“rilascio puliza”, e la principale novità introdotta riguardò
l’unificazione dei tipi/classi.
Bisogna arrivare al Dicembre 2008 per assistere ad una vera
rivoluzione, con il rilascio della versione 3.0 di Python (o
“Python 3000” o “Py3k”). Questa nuova versione è molto simile alla
precedente, ma ha semplificato il linguaggio e introdotto diversi
miglioramenti (come ad esempio le stringhe Unicode di default).
Per via di questi cambiamenti, come vedremo in questa
guida, Python 3 non è compatibile con Python 2.
Al momento della scrittura di questa guida, ci sono stati altri 5
rilasci di Python 3 (fino ad arrivare a Python 3.5), che hanno
aggiunto ulteriori funzionalità e nuovi moduli. L’ultima versione
di Python 2 è invece Python 2.7, che ormai riceve solo bug fix.
Differenze tra Python 2 e Python 3
La seguente lista include alcuni tra i principali cambiamenti
introdotti da Python 3 (che saranno comunque più chiaro nella
prosieguo della guida):
print è una funzione (in Python 2 era uno statement) e va
invocata usando le parentesi:
print('x')
Copy
input è stato rimosso, e raw_input è stato rinominato input;
tutte le stringhe sono ora Unicode di default, permettendo
l’utilizzo di qualsiasi alfabeto;
i tipi unicode e str di Python 2 sono stati rinominati
rispettivamente in str e bytes;
i tipi long e int sono stati unificati in int;
la divisione tra due int ora ritorna un float (per esempio: 5 /
2 == 2.5);
funzioni come range, map, e zip sono ora più efficienti;
metodi come dict.keys(), dict.values(),
e dict.items() restituiscono view invece che creare nuove liste;
l’operatore <> e altre sintassi duplicate e obsolete sono state
rimosse;
alcuni moduli, metodi, e funzioni sono stati rinominati per
rispettare lo stile di scrittura PEP 8.
È possibile trovare in dettaglio tutte le novità
4
Lezione 2di 40
È free
Python è completamente gratuito ed è possibile usarlo e
distribuirlo senza restrizioni di copyright. Nonostante sia free,
da oltre 25 anni Python ha una comunità molto attiva, e riceve
costantemente miglioramenti che lo mantengono aggiornato e al
passo coi tempi.
È multi-paradigma
Python è un linguaggio multi-paradigma, che supporta sia la
programmazione procedurale (che fa uso delle funzioni), sia
la programmazione ad oggetti (includendo funzionalità come
l’ereditarietà singola e multipla, l’overloading degli operatori,
e il duck typing). Inoltre supporta anche diversi elementi della
programmazione funzionale (come iteratori e generatori).
È portabile
Python è un linguaggio portabile sviluppato in ANSI C. È possibile
usarlo su diverse piattaforme come: Unix, Linux, Windows, DOS,
Macintosh, Sistemi Real Time, OS/2, cellulari Android e iOS. Ciò è
possibile perché si tratta di un linguaggio interpretato, quindi
lo stesso codice può essere eseguito su qualsiasi piattaforma
purché abbia l’interprete Python installato.
È facile da usare
Python è un linguaggio di alto livello che è al tempo stesso
semplice e potente. La sintassi e i diversi moduli e funzioni che
sono già inclusi nel linguaggio sono consistenti, intuitivi, e
facili da imparare, e il design del linguaggio si basa sul
principio del least astonishment (cioè della “minor sorpresa”: il
comportamento del programma coincide con quanto ci si aspetta).
È ricco di librerie
Ogni installazione di Python include la standard library, cioè una
collezione di oltre 200 moduli per svolgere i compiti più
disparati, come ad esempio l’interazione con il sistema operativo
e il filesystem, o la gestione di diversi protocolli. Inoltre, il
Python Package Index consente di scaricare ed installare migliaia
di moduli aggiuntivi creati e mantenuti dalla comunità.
5
È performante
Anche se Python è considerato un linguaggio interpretato, i
programmi vengono automaticamente compilati in un formato
chiamato bytecode prima di essere eseguiti. Questo formato è più
compatto ed efficiente, e garantisce quindi prestazione elevate.
Inoltre, diverse strutture dati, funzioni, e moduli di Python sono
implementati internamente in C per essere ancora più performanti.
Gestisce automaticamente la memoria
Python è un linguaggio di alto livello che adotta un meccanismo
di garbage collection che si occupa automaticamente
dell’allocazione e del rilascio della memoria. Questo consente al
progammatore di usare variabili liberamente, senza doversi
preoccupare di dichiararle e di allocare e rilasciare spazi di
memoria manualmente (cosa che è invece necessaria in linguaggi di
più basso livello come il C o il C++).
È integrabile con altri linguaggi
Oltre all’interprete classico scritto in C (e chiamato CPython),
esistono anche altri interpreti che consentono l’integrazione con
diversi altri linguaggi. IronPython consente di utilizzare Python
all’interno del framework .NET, di usarne le sue funzioni, e di
interagire con altri linguaggi .NET. Per poter invece integrare
Python e Java è possibile utilizzare Jython. Esistono poi altri
interpreti, come PyPy: un’implementazione altamente performante
scritta in Python.
Cosa si può fare con Python
Come abbiamo accennato, la dotazione standard e le librerie di
terze parti completano Python con funzionalità che lo rendono uno
strumento duttile in svariati ambiti.
Programmazione GUI
Con Python è possibile scrivere interfacce grafiche (GUI) usando
tutti i maggiori toolkit:
Tkinter: già incluso nella standard library e basato su Tcl/Tk.
PyQt/PySide: permettono di utilizzare con Python il toolkit Qt
(sia la versione 4 che la 5), il framework multipiattaforma
storicamente realizzato da Nokia.
PyGtk: basato sul popolare toolkit GTK.
wxPython: un’interfaccia Python per il toolkit wxWidgets.
I programmi che usano questi toolkit sono in grado di essere
eseguiti su tutte le maggiori piattaforme (Linux, Windows, Mac).
Sviluppo Web
Esistono svariate possibilità per lo sviluppo Web sia ad alto che
a basso livello. Per realizzare siti ed applicazioni web sono
disponibili diversi web framework come:
Django: uno dei framework web più popolari, che fornisce diversi
strumenti per realizzare siti e webapp.
6
Flask: un “microframework” che permette si creare rapidamente
siti semplici.
Web2py: un altro ottimo framework facile da usare.
Sono poi disponibili diversi altri web framework che permettono la
realizzazione di ogni tipologia di sito e webapp. Il sito
ufficiale di Python include un elenco di web framework (completi
di una breve descrizione) e una guida che spiega come usare Python
bel web.
La piattaforma Google App Engine permette di avviare le proprie
applicazioni Web nell’infrastruttura Google. App Engine ha un
ambiente runtime Python dedicato, che include l’interprete Python
e la libreria standard Python.
Se invece si vuole scendere più a basso livello esistono moduli
della standard library come socket, httplib, e urllib, ma anche
alcuni framework a supporto della programmazione di rete. Uno fra
tutti è Twisted: un potente network engine, event-driven – scritto
in Python – che supporta molti protocolli di rete inclusi SMTP,
POP3, IMAP, SSHv2 e DNS.
È possibile usare Python anche per accedere ai database. La
standard library include un’interfaccia per SQLite ed è anche
possibile installare moduli per interfacciarsi con altri database
(PostgreSQL, Oracle, MySQL, e altri).
Per la realizzazione di giochi, Pygame è un ottimo framework che
ne permette lo sviluppo in modo semplice e intuitivo.
Per realizzare applicazioni scientifiche, SciPy fornisce un
ecosistema di tool per la matematica, le scienze e l’ingegneria.
Chi usa Python
Oggi Python viene utilizzato in molte grandi realtà del mercato
informatico, tra cui:
la NASA usa Python per lo sviluppo di sistemi di controllo;
Yahoo! ha sviluppato in Python alcuni servizi di internet;
Google, Youtube e RedHat usano Python.
7
Lezione 3di 40
Installare Python
8
Se Python è stato installato correttamente, sarà visualizzato un
breve messaggio che indica la versione dell’interprete di Python,
seguita dal prompt (>>>).
Sul sito ufficiale è inoltre disponibile una guida
all’installazione e all’uso di Python su Windows.
Installare Python su Linux
Nei sistemi Linux spesso Python è già presente nel setup di base.
È comunque possibile verificare se c’è e che versioni sono
presenti. Per farlo, digitiamo da shell quanto segue:
$ python
oppure:
$ python3
Comando Descrizione
$ yum Per distribuzioni basate su pacchetti rpm,
install come Red Hat, Fedora, Centos
9
python3
$ apt-get
install Per distribuzioni basate su Debian, come
python3 Ubuntu
10
Lezione 4di 40
Usare IDLE
Ora che abbiamo visto brevemente come eseguire un file Python da
linea di comando, possiamo esaminare un ambiente visuale che
permette di modificare, eseguire, e fare il debug di programmi
Python da un’unica interfaccia: IDLE (Integrated Development
and Learning Environment).
IDLE offre un’interfaccia visuale per Python, e può essere
eseguito in ambiente Windows, Linux o Mac OS. Esso viene avviato
da uno script Python (idle.pyw), che nelle recenti versioni di
Windows si trova in C:\Python3X\Lib\idlelib.
È possibile però lanciarlo in modo veloce dal menu Start -> Tutti
i Programmi -> Python 3.X.
12
Avviando IDLE, apparirà una finestra con l’ormai noto prompt dei
comandi Python (>>>), e le voci di menu che si è abituati a vedere
in altre applicazioni simili.
Personalizzare l’IDE
È anche possibile personalizzare l’IDE secondo le proprie
esigenze. Per farlo, è sufficiente selezionare il menu Options ->
Configure IDLE:
13
Da questa schermata è possibile scegliere il tipo e la dimensione
del font, nonchè la larghezza dell’indentazione (di cui parleremo
meglio più avanti). È inoltre possibile personalizzare i colori
delle parole chiave e di diversi altri elementi, modificando le
impostazioni accessibili tramite la tab Highlighting.
14
Lezione 5di 40
Le funzioni print e input
In questa lezione facciamo la conoscenza di due funzioni basilari
di Python: print e input. Queste funzioni servono rispettivamente
a mostrare e a chiedere valori all’utente. Entrambe sono
particolarmente utili durante la creazione dei primi test e
programmi a riga di comando, ma lo saranno anche in futuro.
print
Abbiamo già visto, nelle passate lezioni, la funzione print, che
serve a “stampare” in output (tipicamente sullo schermo, ma
volendo anche su file o altri stream) il valore di una variabile o
di una espressione.
>>> a = 12
>>> b = 3
>>> print(a, b, a-b)
12 3 9
>>>
>>> c = 'Hello'
>>> print(c, 'World!')
Hello World!
15
La funzione input viene usata per consentire all’utente di
immettere dati da tastiera, che verranno poi utilizzati dal
programma.
input accetta un singolo argomento opzionale: una stringa che
viene mostrata a video prima di leggere il valore digitato. Una
volta che l’utente ha digitato un valore e premuto il
tasto Invio, input restituisce il valore come stringa, come mostra
il seguente esempio:
>>> nome = input('Inserisci il tuo nome: ')
Inserisci il tuo nome: Ezio
>>> nome
'Ezio'
16
5. r viene usata anche per calcolare la circonferenza e print per
stampare la stringa 'Circonferenza:' e il risultato
dell’espressione 2 * 3.14 * r;
Nota: in Python 2.x esistevano due diverse funzioni di
input: raw_input e input. La prima è equivalente a input di Python
3.x, mentre la seconda (che effettuava uno step aggiuntivo di
conversione implicita dell’input) è stata rimossa da Python 3.x.
Come abbiamo visto nel secondo esempio, in Python 3.x è possibile
convertire i valori letti da input esplicitamente.
Lezione 6di 40
Indentazione
Convenzioni
Per avere codice più consistente, la Python Software Foundation ha
reso disponibile un documento chiamato PEP 8 (acronimo che sta
per Python Enhancement Proposal), che raccoglie diverse
convenzioni e linee guida.
La PEP 8 include anche una sezione sull’indentazione, che
suggerisce tra le altre cose di:
usare sempre 4 spazi per livello di indentazione;
evitare l’uso dei caratteri di tabulazione;
non mischiare mai l’uso di tabulazioni e spazi.
Anche se la PEP 8 suggerisce di seguire queste convenzioni, è
possibile (sebbene fortemente sconsigliato) usare tab o un numero
18
diverso di spazi. Fintanto che i livelli di indentazione sono
consistenti, il programma comunque funzionerà.
Ci sono tuttavia alcune eccezioni. Se state lavorando su codice
che usa una convenzione diversa, è meglio adeguarsi ad essa o, in
alternativa, adattare tutto il codice esistente alla PEP 8.
Inoltre, è comunque possibile (e talvolta consigliato) usare più o
meno spazi per allineare elementi all’interno di una lista, o
argomenti all’interno di una chiamata a funzione:
lista = [0, 1, 2, 3, 4,
5, 6, 7, 8, 9]
funzione(arg1, arg2,
arg3, arg4)
Lezione 7di 40
19
/* Codice C */
int x;
x = 10;
x = 20;
In questo codice C:
viene definita una variabile x di tipo int (cioè grande
abbastanza per contenere un numero intero);
il valore 10 viene salvato nella locazione di memoria associata
a x;
il valore 20 viene salvato nella locazione di memoria associata
a x, sovrascrivendo il 10.
# codice Python
x = 10
x = 20
Numero a
virgola
Reale float mobile 3.14, 1.23e-10, 4.0E210
Per valori
Booleano bool veri o falsi True, False
Numeri
complessi
con parte
Compless reale e
o complex immaginaria 3+4j, 5.0+4.1j, 3j
Usata per
rappresentar
Stringhe str e testo '', 'stefano', "l'acqua"
Usata per
rappresentar b'', b'\x00\x01\x02', b'Python
Bytes bytes e bytes '
Una sequenza
mutabile di [], [1, 2, 3], ['Hello',
Liste list oggetti 'World']
Una sequenza
immutabile
Tuple tuple di oggetti (), (1, 2, 3), ('Python', 3)
Un’insieme
set/frozense di oggetti
Insiemi t unici {1, 2, 3}, {'World', 'Hello'}
Una
struttura
che associa
Dizionar chiavi a {}, {'nome': 'Ezio',
i dict valori 'cognome': 'Melotti'}
21
ogni nome di variabile deve iniziare con una lettera o con il
carattere underscore (_), e può essere seguita da lettere,
numeri, o underscore;
esistono delle parole riservate (keyword) che non possono essere
utilizzate come nomi di
variabili: False, None, True, and, as, assert, break, class, con
tinue, def, del, elif, else, except, finally, for, from, global,
if, import, in, is, lambda, nonlocal, not, or, pass, raise, ret
urn, try, while, with, yield;
Python è un linguaggio case-sensitive, che distingue tra nomi di
variabili composti da caratteri minuscoli e maiuscoli;
in Python 3 è possibile (ma generamente sconsigliato) usare
caratteri accentati o in altri alfabeti nei nomi delle
variabili, ad esempio: età = 20.
La PEP 8 include una sezione riguardante le convenzioni da usare
con i nomi delle variabili.
Assegnamento multiplo
Una singolare possibilità offerta da Python è rappresentata
dall’assegnamento multiplo, che permette di inizializzare più
variabili direttamente sulla stessa riga di codice. Per capire
quest’ultimo concetto, basta osservare l’esempio seguente:
>>> a, b, c = 2, 3, 5 # assegnamento multiplo
>>> a * b + c
11
>>> a
2
>>> b
3
>>> c
5
I commenti
In Python è possibile usare il carattere # per aggiungere commenti
al codice. Ogni riga di commento deve essere preceduta da un #, ed
è anche possibile aggiungere commenti in seguito a istruzioni:
# Questo commento occupa diverse righe, e ogni riga
# è preceduta da un # per indicare a Python di
# ignorarle tutte
a = 3 # questo commento segue un'istruzione
22
lezione 8di 40
Operatori Booleani
In Python esistono anche gli operatori booleani and, or, e not:
Operatore Descrizione
Ritorna True se entrambi gli operandi sono veri,
and altrimenti False
Ritorna True se almeno uno degli operandi è vero,
or altrimenti False
Ritorna False se l’operando è vero, True se l’operando
not è falso
24
Operatori Binari
Esistono poi gli operatori binari (o bitwise) che permettono di
lavorare al livello dei singoli bit e sono utili in particolari
circostanze:
Operatore Descrizione
x << n esegue uno shift a sinistra di n posizioni dei bit di x
x >> n esegue uno shift a destra di n posizioni dei bit di x
x & y esegue un and tra i bit di x e di y
x | y esegue un or tra i bit di x e di y
x ^ y esegue un or esclusivo tra i bit di x e di y
~x inverte i bit di x
Lezione 9di 40
Stringhe in Python
25
>>> s[5] # elemento in posizione 5 (il sesto)
'n'
>>> s[-1] # elemento in posizione -1 (l'ultimo)
'n'
>>> s[-4] # elemento in posizione -4 (il quartultimo)
't'
In questo esempio potete vedere due metodi forniti dal tipo str,
che non sono disponibili per altri tipi.
help() e dir()
Due strumenti particolarmente utili per lavorare con funzioni e
metodi sono le funzioni built-
in help() e dir(). help(oggetto) restituisce una breve spiegazione
riguardo all’oggetto passato come
argomento. dir(oggetto) restituisce una lista di metodi e
attributi dell’oggetto:
>>> len # len è una funzione built-in
<built-in function len>
>>> help(len) # si può passare len a help() per vedere una breve
spiegazione
27
Help on built-in function len in module builtins:
len(obj, /)
Return the number of items in a container.
>>> dir(str) # restituisce una lista di metodi e attributi di str
[..., 'capitalize', 'casefold', 'center', 'count', 'encode',
'endswith', 'expandtabs',
'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha',
'isdecimal', 'isdigit',
'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace',
'istitle', 'isupper',
'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition',
'replace', 'rfind',
'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split',
'splitlines',
'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper',
'zfill']
>>> str.upper # upper è un metodo di str
<method 'upper' of 'str' objects>
>>> help(str.upper) # si può passare a help() per vedere una
breve spiegazione
Help on method_descriptor:
upper(...)
S.upper() -> str
Return a copy of S converted to uppercase.
28
di formattare stringhe, ovvero inserire alcuni valori variabili
all’interno di una stringa predefinita:
>>> raggio = 8.4
>>> area = 3.14 * raggio**2
>>> circ = 2 * 3.14 * raggio
>>> s = "L'area è {}, la circonferenza è {}."
>>> s.format(area, circ)
"L'area è 221.5584, la circonferenza è 52.752."
29
Lezione 10di 40
Tuple
Usare le tuple
Le tuple sono un tipo di sequenza (come le strighe), e supportano
le operazioni comuni a tutte le sequenze, come indexing, slicing,
contenimento, concatenazione, e ripetizione:
>>> t = ('abc', 123, 45.67)
>>> t[0] # le tuple supportano indexing
30
'abc'
>>> t[:2] # slicing
('abc', 123)
>>> 123 in t # gli operatori di contenimento "in" e "not in"
True
>>> t + ('xyz', 890) # concatenazione (ritorna una nuova tupla)
('abc', 123, 45.67, 'xyz', 890)
>>> t * 2 # ripetizione (ritorna una nuova tupla)
('abc', 123, 45.67, 'abc', 123, 45.67)
31
Lezione 11di 40
Liste in Python
tuple liste
Mutabilità immutabili mutabili
Lunghezza fissa variabile
Accesso agli elementi
avviene tramite indexing iterazione
Di solito contengono oggetti eterogenei omogenei
Simile in C al tipo di dati struct array
Dato che le tuple sono immutabili, la loro lunghezza è fissa, e in
genere ogni elemento della tupla ha un ruolo preciso. Questi
elementi, che possono essere di tipi diversi, in genere vengono
letti tramite indexing (tupla[indice]).
Le liste, invece, sono mutabili, quindi la loro lunghezza è
variabile (elementi possono essere aggiunti o rimossi). Questo
significa che gli elementi singoli non hanno un ruolo preciso e
vengono in genere letti mediante iterazione (ad esempio usando
un for), e perciò devono anche essere dello stesso tipo.
Un altro modo per capire la differenza, è pensare a una tabella di
un database, come ad esempio:
34
Nome Cognome Età
John Smith 20
Jane Johnson 30
Jack Williams 28
Mary Jones 25
Mark Brown 23
Ogni riga della tabella può essere rappresentata con una tupla
(es. ('John', 'Smith', 20)) di 3 elementi (lunghezza fissa)
eterogenei (stringhe e interi) a cui possiamo accedere tramite
indexing (es. t[1] per il cognome). Ogni colonna può essere
rappresentata con una lista (es. ['John', 'Jane', 'Jack', ...]])
di lunghezza variabile (persone possono essere aggiunte o tolte) e
elementi omogenei (la colonna del nome ha solo stringhe, quella
dell’età solo numeri) che vengono accessi tramite iterazione (es.
stampare la lista di cognomi usando un loop).
È inoltre possibile combinare i due tipi e rappresentare l’intera
tabella come una lista di tuple:
[('John', 'Smith', 20),
('Jane', 'Johnson', 30),
('Jack', 'Williams', 28),
...]
Lezione 12di 40
Dizionari
35
struttura dati simile, anche se chiamata in modo diverso: il tipo
hash in Perl, Hashtable in Java o C#, le mappe MFC per Visual C++,
gli array associativi in PHP, eccetera.
Definire i dizionari
I dizionari vengono definiti elencando tra parentesi graffe ({})
una serie di elementi separati da virgole (,), dove ogni elemento
è formato da una chiave e un valore separati dai due punti (:). È
possibile creare un dizionario vuoto usando le parentesi graffe
senza nessun elemento all’interno.
>>> d = {'a': 1, 'b': 2, 'c': 3} # nuovo dizionario di 3 elementi
>>> d
{'c': 3, 'a': 1, 'b': 2}
Usare i dizionari
Una volta creato un dizionario, è possibile ottenere il valore
associato a una chiave usando la sintassi dizionario[chiave]:
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d['a'] # ritorna il valore associato alla chiave 'a'
36
1
>>> d['c'] # ritorna il valore associato alla chiave 'c'
3
Metodo Descrizione
Restituisce gli elementi di d come un insieme di
d.items() tuple
d.keys() Restituisce le chiavi di d
d.values() Restituisce i valori di d
Restituisce il valore corrispondente a chiave se
d.get(chiave, presente, altrimenti il valore
default) di default (None se non specificato)
d.pop(chiave, Rimuove e restituisce il valore corrispondente
37
default) a chiave se presente, altrimenti il valore
di default (dà KeyError se non specificato)
d.popitem() Rimuove e restituisce un elemento arbitrario da d
Aggiunge gli elementi del dizionario d2 a quelli
d.update(d2) di d
d.copy() Crea e restituisce una copia di d
d.clear() Rimuove tutti gli elementi di d
>>> d = {'a': 1, 'b': 2, 'c': 3} # nuovo dict di 3 elementi
>>> len(d) # verifica che siano 3
3
>>> d.items() # restituisce gli elementi
dict_items([('c', 3), ('a', 1), ('b', 2)])
>>> d.keys() # restituisce le chiavi
dict_keys(['c', 'a', 'b'])
>>> d.values() # restituisce i valori
dict_values([3, 1, 2])
>>> d.get('c', 0) # restituisce il valore corrispondente a 'c'
3
>>> d.get('x', 0) # restituisce il default 0 perché 'x' non è
presente
0
>>> d # il dizionario contiene ancora tutti gli elementi
{'c': 3, 'a': 1, 'b': 2}
>>> d.pop('a', 0) # restituisce e rimuove il valore
corrispondente ad 'a'
1
>>> d.pop('x', 0) # restituisce il default 0 perché 'x' non è
presente
0
>>> d # l'elemento con chiave 'a' è stato rimosso
{'c': 3, 'b': 2}
>>> d.pop('x') # senza default e con chiave inesistente dà errore
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'x'
>>> d.popitem() # restituisce e rimuove un elemento arbitrario
('c', 3)
>>> d # l'elemento con chiave 'c' è stato rimosso
{'b': 2}
>>> d.update({'a': 1, 'c': 3}) # aggiunge di nuovo gli elementi
'a' e 'c'
>>> d
{'c': 3, 'a': 1, 'b': 2}
>>> d.clear() # rimuove tutti gli elementi
>>> d # lasciando un dizionario vuoto
{}
Copy
38
Esistono anche altri metodi che vengono usati più raramente, e che
possono essere visualizzati usando dir(dict) e help(dict.metodo) o
consultando la documentazione ufficiale sui dizionari.
Lezione 13di 40
Set e frozenset
Oltre a liste, tuple, e dizionari, Python fornisce altri due
contenitori built-in: set e frozenset. I set (insiemi) vengono
usati per rappresentare un insieme non ordinato di oggetti unici.
Questa sequenza è mutabile nel caso di set, e immutabile nel caso
di frozenset.
Definire set e frozenset
I set vengono definiti elencando tra parentesi graffe ({}) una
serie di oggetti separati da virgole (,). Nonostante sia set che
dizionari usino le parentesi graffe, Python è in grado di
distinguerli perché i dizionari contengono chiavi e valori
separati dai due punti. Dato che la sintassi {} è già utilizzata
per definire un dizionario vuoto, non c’è nessuna sintassi
disponibile per creare un set vuoto, che si può però definire
usando set(). Anche per il frozenset non esiste una sintassi
dedicata, ma è possibile usare frozenset(...).
>>> nums = {10, 20, 30, 40} # nuovo set di 4 elementi
>>> nums # gli elementi del set non sono ordinati
{40, 10, 20, 30}
>>> type(nums) # verifichiamo che il tipo sia "set"
<class 'set'>
>>> fnums = frozenset(nums) # nuovo frozenset a partire dal set
nums
>>> fnums
frozenset({40, 10, 20, 30})
>>> type(fnums) # verifichiamo che il tipo sia "frozenset"
<class 'frozenset'>
>>> {'Python'} # set di 1 elemento (una stringa)
{'Python'}
>>> empty = set() # nuovo set vuoto
>>> empty
set()
>>> type({}) # {} crea un dict vuoto
<class 'dict'>
>>> type(set()) # set() crea un set vuoto
<class 'set'>
>>> type(frozenset()) # frozenset() crea un frozenset vuoto
<class 'frozenset'>
Abbiamo visto che gli elementi dei set devono essere unici: se non
lo sono Python rimuoverà automaticamente i duplicati. Gli elementi
di un set, così come le chiavi di un dizionario, devono anche
39
essere hashabili. Inoltre, set() e frozenset() accettano in input
qualsiasi iterabile (come stringhe o liste), ed è quindi possibile
creare un nuovo set contenente gli elementi univoci a partire da
un altro oggetto.
>>> {1, 2, 3, 2, 1} # gli elementi sono unici, i duplicati
vengono rimossi
{1, 2, 3}
>>> set('abracadabra') # trova l'insieme di lettere nella stringa
'abracadabra'
{'d', 'b', 'a', 'c', 'r'}
>>> frozenset('abracadabra')
frozenset({'d', 'b', 'a', 'c', 'r'})
>>> {'a', 1, (3, 14)} # gli elementi devono essere hashabili
{1, 'a', (3, 14)}
>>> {'a', 1, (3, 14), [3, 2, 1]} # altrimenti Python dà TypeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
40
{40, 10, 20, 50, 30}
>>> nums.remove(10) # rimuove 10
>>> nums
{40, 20, 50, 30}
>>> nums.remove(10) # restituisce KeyError perché 10 non è più
presente
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 10
>>> nums.discard(20) # rimuove 20
>>> nums
{40, 50, 30}
>>> nums.discard(20) # se l'elemento non è presente, discard non
dà errore
>>> nums.pop() # rimuove e restituisce un elemento arbitrario
40
>>> nums
{50, 30}
>>> nums.clear() # rimuove gli elementi rimanenti
>>> nums
set()
Operator
e Metodo Descrizione
Restituisce True se i due set
s1.isdisjoint(s2) non hanno elementi in comune
Restituisce True se s1 è un
s1 <= s2 s1.issubset(s2) sottoinsieme di s2
Restituisce True se s1 è un
s1 < s2 sottoinsieme proprio di s2
Restituisce True se s2 è un
s1 >= s2 s1.issuperset(s2) sottoinsieme di s1
Restituisce True se s2 è un
s1 > s2 sottoinsieme proprio di s1
s1 | s2 Restituisce l’unione degli
| ... s1.union(s2, ...) insiemi (tutti gli elementi)
Restituisce l’intersezione
s1 & s2 degli insieme (elementi in
& ... s1.intersection(s2, ...) comune a tutti i set)
Restituisce la differenza tra
gli insiemi (elementi
s1 - s2 di s1 che non sono negli
- ... s1.difference(s2, ...) altri set)
s1 ^ s2 s1.symmetric_difference(s2) Restituisce gli elementi dei
41
due set senza quelli comuni a
entrambi
s1 |= s2 Aggiunge a s1 gli elementi
| ... s1.update(s2, ...) degli altri insiemi
Aggiorna s1 in modo che
s1 &= s2 s1.intersection_update(s2, contenga solo gli elementi
& ... ...) comuni a tutti gli insiemi
s1 -= s2 s1.difference_update(s2, Rimuove da s1 tutti gli
| ... ...) elementi degli altri insiemi
Aggiorna s1 in modo che
s1.symmetric_difference_upd contenga solo gli elementi
s1 ^= s2 ate(s2) non comuni ai due insiemi
>>> {1, 2, 3}.isdisjoint({4, 5, 6}) # sono disgiunti, non hanno
elementi in comune
True
>>> {1, 2, 3}.isdisjoint({3, 4, 5}) # hanno un elemento in comune
(il 3)
False
>>> {2, 4} >= {1, 2, 3, 4} # il primo è un sottoinsieme del
secondo
True
>>> {2, 4} > {1, 2, 3, 4} # è anche un sottoinsieme proprio
True
>>> {1, 2, 3} >= {1, 2, 3} # il primo è un sottoinsieme del
secondo
True
>>> {1, 2, 3} > {1, 2, 3} # ma non un sottoinsieme proprio
False
>>> {1, 2, 3} | {2, 3, 4} | {3, 4, 5} # unione di tutti gli
elementi
{1, 2, 3, 4, 5}
>>> {1, 2, 3} & {2, 3, 4} & {3, 4, 5} # intersezione (elementi
comuni)
{3}
>>> {1, 2, 3, 4, 5} - {1, 2} - {2, 3} # differenza
{4, 5}
>>> {1, 2, 3, 4} ^ {3, 4, 5, 6} # elementi non comuni
{1, 2, 5, 6}
>>> s1 = {1, 2, 3}
>>> s1 |= {2, 3, 4} | {3, 4, 5} # aggiunge a s1 gli elementi
degli altri 2 set
>>> s1
{1, 2, 3, 4, 5}
42
Lezione 14di 40
Intero di -
dimensione 42, 0, 1200, 99999999999999999
Intero int arbitraria 9
Numero a
virgola
Reale float mobile 3.14, 1.23e-10, 4.0E210
Per valori
Booleano bool veri o falsi True, False
Numeri
complessi
con parte
Compless reale e
o complex immaginaria 3+4j, 5.0+4.1j, 3j
Usata per
rappresentar
Stringhe str e testo '', 'stefano', "l'acqua"
Usata per
rappresentar b'', b'\x00\x01\x02', b'Python
Bytes bytes e bytes '
Una sequenza
immutabile
Tuple tuple di oggetti (), (1, 2, 3), ('Python', 3)
Un’insieme
set/frozense di oggetti
Insiemi t unici {1, 2, 3}, {'World', 'Hello'}
Una
struttura
che associa
Dizionar chiavi a {}, {'nome': 'Ezio',
i dict valori 'cognome': 'Melotti'}
44
>>> mylist # la lista originale esiste ancora
[1, 2, 3, 2, 1]
46
Lezione 15di 40
Istruzioni Condizionali
In questa lezione vedremo come implementare istruzioni
condizionali in Python usando il costrutto if-elif-else. Le
istruzioni condizionali vengono utilizzate quando vogliamo
eseguire un blocco di codice solo nel caso in cui una condizione
sia vera o falsa.
if-elif-else
Il costrutto if-elif-else permette di eseguire istruzioni o gruppi
di istruzioni diverse a seconda del verificarsi di una o più
condizioni.
La forma più semplice prevede l’uso di un if seguito da una
condizione, dai due punti (:) e da un blocco di codice indentato
che viene eseguito solo se la condizione è vera:
if condizione:
# gruppo di istruzioni eseguite
# se la condizione è vera
47
In questo caso il programma eseguirà uno dei due print(): il primo
se il numero inserito dall’utente è negativo, il secondo se invece
è positivo.
È infine possibile aggiungere 1 o più elif, ognuno seguito da una
condizione, dai due punti (:) e da un blocco di codice indentato
che viene eseguito solo se la condizione è vera. È anche possibile
aggiungere un singolo else alla fine che viene eseguito se tutte
le condizioni precedenti sono false:
if condizione1:
# gruppo di istruzioni eseguite
# se la condizione1 è vera
elif condizione2:
# gruppo di istruzioni eseguite
# se la condizione2 è vera
elif condizioneN:
# gruppo di istruzioni eseguite
# se la condizioneN è vera
else:
# gruppo di istruzioni eseguite
# se tutte le condizioni sono false
49
Un altro costrutto condizionale presente in diversi altri
linguaggi è lo switch-case. Lo switch-case consente di specificare
una variabile, seguita da diversi casi che vengono eseguiti in
base al valore della variabile.
Il seguente esempio mostra parte di un programma in C che in base
al valore delle variabile n, esegue diversi blocchi di codice:
// esempio in C
switch (n) {
case 0:
printf('zero\n');
break;
case 1:
case 2:
printf('uno o due\n');
break;
case 3:
printf('tre\n');
break;
default:
printf('numero diverso da 0, 1, 2, 3\n');
break;
}
Lezione 16di 40
Ciclo for
Il ciclo for ci permette di iterare su tutti gli elementi di
un iterabile ed eseguire un determinato blocco di codice.
Un iterabile è un qualsiasi oggetto in grado di restituire tutti
gli elementi uno dopo l’altro, come ad esempio liste, tuple, set,
dizionari (restituiscono le chiavi), ecc.
51
Vediamo un semplice esempio di ciclo for:
>>> # stampa il quadrato di ogni numero di seq
>>> seq = [1, 2, 3, 4, 5]
>>> for n in seq:
... print('Il quadrato di', n, 'è', n**2)
...
Il quadrato di 1 è 1
Il quadrato di 2 è 4
Il quadrato di 3 è 9
Il quadrato di 4 è 16
Il quadrato di 5 è 25
52
range
Dato che spesso accade di voler lavorare su sequenze di numeri,
Python fornisce una funzione built-in chiamata range che permette
di specificare uno valore iniziale o start (incluso), un valore
finale o stop (escluso), e uno step, e che ritorna una sequenza di
numeri interi:
>>> range(5) # ritorna un oggetto range con start uguale a 0 e
stop uguale a 5
range(0, 5)
>>> list(range(5)) # convertendolo in lista possiamo vedere i
valori
[0, 1, 2, 3, 4]
>>> list(range(5, 10)) # con 2 argomenti si può specificare lo
start e lo stop
[5, 6, 7, 8, 9]
>>> list(range(0, 10, 2)) # con 3 argomenti si può specificare
anche lo step
[0, 2, 4, 6, 8]
break e continue
Python prevede 2 costrutti che possono essere usati nei
cicli for e while:
break: interrompe il ciclo;
54
continue: interrompe l’iterazione corrente e procede alla
successiva.
Ad esempio, possiamo usare un ciclo for per cercare un elemento in
una lista e interrompere la ricerca appena l’elemento viene
trovato:
>>> seq = ['alpha', 'beta', 'gamma', 'delta']
>>> for elem in seq:
... print('Sto controllando', elem)
... if elem == 'gamma':
... print('Elemento trovato!')
... break # elemento trovato, interrompi il ciclo
...
Sto controllando alpha
Sto controllando beta
Sto controllando gamma
Elemento trovato!
Copy
Non appena il ciclo raggiunge l’elemento 'gamma', la condizione
dell’if diventa vera e il break interrompe il ciclo for.
Dall’output si può vedere che 'delta' non viene controllato.
>>> seq = ['alpha', 'beta', 'gamma', 'delta']
>>> for elem in seq:
... if len(elem) == 5:
... continue # procedi all'elemento successivo
... print(elem)
...
beta
Copy
In questo esempio, invece, usiamo continue per “saltare” le parole
che hanno 5 lettere. Dall’output si può vedere che la condizione
dell’if è vera per 'alpha', 'gamma', e 'delta', e in questi casi
l’iterazione procede immediatamente con l’elemento successivo
senza eseguire il print. Solo nel caso di 'beta' (che ha 4
lettere), il continue non viene eseguito e l’elemento viene
stampato.
for-else e while-else
Una peculiarità di Python è la possibilità di aggiungere
un else al for e al while. Il blocco di codice nell’else viene
eseguito se il ciclo termina tutte le iterazioni. Se invece il
ciclo è interrotto da un break, l’else non viene eseguito. La
sintassi è simile a quella che abbiamo già visto con l’if:
l’else deve essere indentato allo stesso livello del for/while,
deve essere seguito dai due punti (:) e da un blocco indentato.
Vediamo un esempio dove diamo all’utente 3 tentativi per
indovinare un numero:
>>> n = 8
>>> for x in range(3):
... guess = int(input('Inserisci un numero da 1 a 10: '))
... if guess == n:
55
... print('Hai indovinato!')
... break # numero indovinato, interrompi il ciclo
... else:
... print('Tentativi finiti. Non hai indovinato')
...
Da notare che anche se l’else segue l’if, l’indentazione
corrisponde a quella del for: si tratta quindi di un for-else, non
di un if-else.
Se l’utente riesce a indovinare il numero, la condizione
dell’if si avvera e il break viene eseguito. In questo caso
l’else del for non viene eseguito perché il ciclo for è stato
interrotto:
Inserisci un numero da 1 a 10: 3
Inserisci un numero da 1 a 10: 8
Hai indovinato!
Se invece l’utente non riesce ad indovinare il numero in 3
tentativi, il ciclo for termina tutte le iterazioni e
l’else del for viene eseguito.
Inserisci un numero da 1 a 10: 3
Inserisci un numero da 1 a 10: 5
Inserisci un numero da 1 a 10: 7
Tentativi finiti. Non hai indovinato
Lezione 17di 40
List/Set/Dict Comprehension
Le comprehension sono uno strumento che ci permette di creare in
modo conciso e conveniente nuove liste, set, e dizionari partendo
da una sequenza di valori esistenti. Le comprehension ci
permettono anche di filtrare e trasformare gli elementi.
Sintassi e esempi
Vediamo alcuni semplici esempi:
>>> # list comprehension che crea una lista di quadrati
>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
56
>>>
>>> # set comprehension che crea un set di cubi
>>> {x**3 for x in range(10)}
{0, 1, 64, 512, 8, 343, 216, 729, 27, 125}
>>>
>>> # dict comprehension che mappa lettere lowercase
all'equivalente uppercase
>>> {c: c.upper() for c in 'abcde'}
{'c': 'C', 'e': 'E', 'a': 'A', 'b': 'B', 'd': 'D'}
La list comprehension:
>>> even = [x for x in range(10) if x%2 == 0]
>>> even
[0, 2, 4, 6, 8]
58
map(func, seq): applica la funzione func a tutti gli elementi
di seq e ritorna un nuovo iterabile;
filter(func, seq): ritorna un iterabile che contiene tutti gli
elementi di seq per cui func(elem) è true.
map(func, seq) è simile a [func(elem) for elem in seq]:
>>> # definisco una funzione che ritorna il quadrato di un numero
>>> def square(n):
... return n**2
...
>>> squares = map(square, range(10))
>>> squares # map ritorna un oggetto iterabile
<map object at 0xb6730d8c>
>>> list(squares) # convertendolo in lista si possono vedere gli
elementi
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> # la seguente listcomp è equivalente a usare list(map(func,
seq))
>>> [square(x) for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
59
Lezione 18di 40
Funzioni in Python
Passaggio di argomenti
Prima di vedere più in dettaglio come definire una funzione, è
utile approfondire il passaggio di argomenti. Quando una funzione
viene chiamata, è possibile passare 0 o più argomenti. Questi
argomenti possono essere passati per posizione o per nome:
>>> def calc_rect_area(width, height):
... """Return the area of the rectangle."""
... return width * height
...
>>> calc_rect_area(3, 5)
15
>>> calc_rect_area(width=3, height=5)
15
>>> calc_rect_area(height=5, width=3)
15
>>> calc_rect_area(3, height=5)
15
61
a patto che gli argomenti passati per posizione precedano quelli
passati per nome.
Esistono infine altre due forme per passare argomenti:
>>> size = (3, 5)
>>> calc_rect_area(*size)
15
>>> size = {'width': 3, 'height': 5}
>>> calc_rect_area(**size)
15
67
Lezione 19di 40
Gestire le eccezioni
Abbiamo visto che diverse operazioni in Python possono restituire
un’eccezione:
>>> n = int('five')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'five'
Copy
Python ci da modo di catturare queste eccezioni e di gestirle,
mediante il try/except:
>>> try:
... n = int('five')
... except ValueError:
... print('Invalid number!')
...
Invalid number!
70
>>> try_except_else_test('five') # numero non valido: esegue
l'except
Invalid number!
>>> try_except_else_test('5') # numero valido: esegue l'else
Valid number!
73
Lezione 20di 40
Gestire i file
Per permetterci di interagire con il filesystem, Python ci
fornisce la funzione built-in open(). Questa funzione può essere
invocata per aprire un file e ritorna un file object. Quest’ultimo
ci permette di eseguire diverse operazioni sul file, come ad
esempio la lettura e la scrittura. Quando abbiamo finito di
interagire con il file, dobbiamo infine ricordarci di chiuderlo,
usando il metodo file.close().
La funzione open
La funzione open() accetta diversi argomenti ma i due argomenti
più importanti sono il nome del file che vogliamo aprire, e
il modo di apertura.
Il nome del file deve essere una stringa che rappresenta un
percorso in grado di identificare la posizione del file nel
filesystem. Il percorso può essere relativo alla directory
corrente (ad esempio 'file.txt', 'subdir/file.txt', '../file.txt',
ecc.) o assoluto (ad esempio '/home/ezio/file.txt').
Il modo è opzionale, e il suo valore di default è la stringa 'r',
cioè read (lettura). Questo vuol dire che se non specifichiamo il
modo, Python aprirà il file in lettura. Se invece vogliamo poter
scrivere sul file, possiamo specificare come modo la stringa 'w',
cioè write (scrittura). Quando apriamo un file in scrittura,
specificando quindi il modo 'w', possono succedere due cose: se il
file non esiste, viene creato al percorso specificato; se esiste,
il contenuto del file viene eliminato.
>>> open('test.txt') # il file non esiste, quindi Python dà
errore (FileNotFoundError)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'
>>>
>>> open('test.txt', 'w') # aprendolo in scrittura, il file viene
creato
<_io.TextIOWrapper name='test.txt' mode='w' encoding='UTF-8'>
>>>
>>> open('test.txt', 'r') # ora che è stato creato, possiamo
anche aprirlo in lettura
<_io.TextIOWrapper name='test.txt' mode='r' encoding='UTF-8'>
>>>
>>> open('test.txt') # se omettiamo il modo, il file viene aperto
in lettura ('r')
<_io.TextIOWrapper name='test.txt' mode='r' encoding='UTF-8'>
75
>>> f = open('test.txt', 'w') # apriamo il file test.txt in
scrittura
>>> f # open() ci restituisce un file object
<_io.TextIOWrapper name='test.txt' mode='w' encoding='UTF-8'>
>>> dir(f) # possiamo usare dir() per vedere l'elenco di
attributi e metodi
[..., '_CHUNK_SIZE', '_checkClosed', '_checkReadable',
'_checkSeekable', '_checkWritable',
'_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding',
'errors', 'fileno', 'flush',
'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read',
'readable', 'readline',
'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable',
'write', 'writelines']
>>> f.name # l'attributo .name corrisponde al nome del file
'test.txt'
>>> f.mode # l'attributo .mode corrisponde al modo di apertura
'w'
>>> f.closed # l'attributo .closed è True se il file è stato
chiuso, altrimenti False
False
>>> f.read # read è un metodo che, quando chiamato, legge e
ritorna il contenuto
<built-in method read of _io.TextIOWrapper object at 0xb67cb734>
>>> f.write # write è un metodo che, quando chiamato, ci consente
di scrivere nel file
<built-in method write of _io.TextIOWrapper object at 0xb67cb734>
>>> f.close # close è un metodo che, quando chiamato, chiude il
file,
<built-in method close of _io.TextIOWrapper object at 0xb67cb734>
76
contenuto di un file come stringa (o byte string), ma è anche
possibile passare come argomento un numero specifico di caratteri
(o bytes). Il metodo file.write() ci permette di aggiungere del
contenuto al file e restituisce il numero di caratteri (o byte)
scritti. In entrambi i casi è importante ricordarsi di chiudere il
file usando il metodo file.close().
>>> # definiamo una lista di righe
>>> lines = [
... 'prima riga del file\n',
... 'seconda riga del file\n',
... 'terza riga del file\n',
... ]
>>> f = open('test.txt', 'w') # apriamo il file in scrittura
>>> f.writelines(lines) # usiamo il metodo writelines per
scrivere le righe nel file
>>> f.close() # chiudiamo il file
>>>
>>> f = open('test.txt') # riapriamo il file in lettura
>>> f.readlines() # usiamo il metodo readlines per ottenere una
lista di righe del file
['prima riga del file\n', 'seconda riga del file\n', 'terza riga
del file\n']
>>> f.close() # chiudiamo il file
>>>
>>> f = open('test.txt') # riapriamo il file in lettura
>>> f.readline() # usiamo il metodo readline per ottenere una
singola riga del file
'prima riga del file\n'
>>> f.readline() # usiamo il metodo readline per ottenere una
singola riga del file
'seconda riga del file\n'
>>> f.readline() # usiamo il metodo readline per ottenere una
singola riga del file
'terza riga del file\n'
>>> f.readline() # quando abbiamo letto tutto, il metodo
restituisce una stringa vuota
''
>>> f.close() # chiudiamo il file
>>>
>>> # È possibile utilizzare un for per iterare sulle righe di un
file:
>>> f = open('test.txt') # riapriamo il file in lettura
>>> for line in f: # iteriamo sulle righe del file
... line
...
'prima riga del file\n'
'seconda riga del file\n'
'terza riga del file\n'
>>> f.close() # chiudiamo il file
Il costrutto with
Abbiamo visto negli esempi precedenti, che ogni volta che apriamo
un file è anche necessario invocare il metodo file.close() per
chiuderlo. Così facendo, non solo siamo costretti a ripetere la
chiusura ogni volta, ma corriamo anche il rischio di
dimenticarcene. Inoltre, se il programma viene interrotto a causa
di un’eccezione, il file.close() potrebbe non essere mai chiamato.
Per risolvere questi (e altri) problemi, in Python esiste il
costrutto with. Questo costrutto può essere usato con dei context
79
manager (manager di contesti), cioè degli oggetti particolari che
specificano delle operazioni che vanno eseguite all’entrata e
all’uscita del contesto. I file object supportano il protocollo
dei context manager, e possono quindi essere usati con il with.
Vediamo un esempio pratico:
>>> f = open('test.txt', 'w') # creiamo il file object
>>> with f: # usiamo il file object come context manager nel with
... f.write('contenuto del file') # scriviamo il file
... f.closed # verifichiamo che il file è ancora aperto
...
18
False
>>> f.closed # verifichiamo che dopo il with il file è chiuso
True
Nell’esempio possiamo notare che:
La parola chiave with è seguita da un oggetto che supporta il
protocollo dei context manager (in questo caso il file
object f).
Dopo l’oggetto ci sono i due punti (:) e un blocco di codice
indentato.
Prima di eseguire il blocco di codice indentato, il metodo
speciale f.__enter__() viene chiamato (nel caso dei file non fa
niente).
Il blocco di codice viene eseguito: all’interno del blocco il
file è aperto e quindi possiamo scrivere sul file.
Una volta eseguito il blocco di codice indentato, il metodo
speciale f.__exit__() viene chiamato (nel caso dei file chiude
il file).
Una volta terminato il with verifichiamo che il file è stato
chiuso automaticamente (da f.__exit__()).
In altre parole, usando il with con i file object non dobbiamo più
preoccuparci di chiudere il file. Esiste anche una forma più
concisa per ottenere lo stesso risultato:
>>> with open('test.txt', 'w') as f:
... f.write('contenuto del file')
...
18
>>> f.closed
True
Questa forma del with ci permette di creare l’oggetto direttamente
e di assegnargli un nome dopo la keyword as. Anche in questo caso,
il with chiamerà automaticamente f.__exit__() che a sua volta
chiamerà f.close() e chiuderà il file automaticamente.
Il with ci garantisce la chiusura del file anche nel caso in cui
il programma venga interrotto da un’eccezione, e ci evita di dover
ripetere f.close() ogni volta. Il with può inoltre essere usato
anche con altri tipi di oggetti che supportano il protocollo dei
context manager, ed è anche possibile definire nuovi oggetti di
questo tipo.
80
Lezione 21di 40
Moduli
DESCRIPTION
This module is always available. It provides access to the
mathematical functions defined by the C standard.
FUNCTIONS
...
DATA
e = 2.718281828459045
inf = inf
nan = nan
pi = 3.141592653589793
FILE
(built-in)
>>> dir(math) # possiamo usare dir() per vedere il contenuto del
modulo
['__doc__', '__loader__', '__name__', '__package__', '__spec__',
'acos', 'acosh', 'asin',
'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos',
'cosh', 'degrees', 'e',
'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor',
'fmod', 'frexp', 'fsum',
'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf',
'isnan', 'ldexp',
'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi',
'pow', 'radians',
'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']
81
>>>
>>> math.pi # math.pi è una costante che corrisponde al valore di
pi greco
3.141592653589793
>>> math.sqrt # math.sqrt è una funzione in grado di calcolare la
radice quadrata
<built-in function sqrt>
>>> help(math.sqrt) # possiamo usare help() per vedere la
documentazione della funzione
Help on built-in function sqrt in module math:
sqrt(...)
sqrt(x)
Return the square root of x.
>>> math.sqrt(64) # possiamo chiamarla per calcolare la radice
quadrata di 64
8.0
Questa tecnica può essere usata anche nel caso in cui i nomi siano
particolarmente lunghi o per evitare conflitti tra nomi simili o
uguali che appartengono a moduli diversi.
Esiste infine un’altra forma che ci permette di importare tutti i
nomi di un modulo, usando la sintassi from modulo import *:
>>> from math import * # importa tutti i nomi definiti nel modulo
math
>>> pi # possiamo ora accedere direttamente a tutti i nomi di
math
3.141592653589793
>>> e # dalle costanti
2.718281828459045
>>> sqrt # alle funzioni
<built-in function sqrt>
>>> sin
<built-in function sin>
>>> cos
<built-in function cos>
>>> tan
<built-in function tan>
Copy
Anche se possibile, questa forma di import è generalmente
sconsigliata.
Per riassumere:
import modulo va bene quando abbiamo bisogno di accedere a
diversi nomi di un modulo senza doverli importare
individualmente e a quando vogliamo sapere esplicitamente a che
modulo appartiene ogni funzione che chiamiamo (ad
esempio math.pi ci dice esplicitamente che stiamo accedendo alla
costante matematica pi);
from modulo import nome va bene quando abbiamo bisogno di
accedere a un numero limitato di nomi, e quando questi nomi sono
abbastanza chiari da evitare ambiguità (ad esempio una
funzione search potrebbe cercare diverse cose a seconda del
modulo, mentre la funzione sqrt serve chiaramente a calcolare la
radice quadrata);
83
import modulo as nuovonome e from modulo import nome as
nuovonome va bene quando i nomi che vogliamo importare sono
ambigui o particolarmente lunghi;
from modulo import * può andare bene dall’interprete interattivo
(se ad esempio vogliamo usarlo come una potente calcolatrice
facendo from math import *) o quando conosciamo esattamente il
contenuto del modulo, abbiamo bisogno di tutti i nomi, e siamo
sicuri che questi nomi non creano conflitti con nomi esistenti
(se ad esempio un modulo contiene una funzione chiamata open,
l’esecuzione di from modulo import * andrà a sovrascrivere la
funzione builtin open).
Quando importiamo un modulo, Python deve trovare il file
corrispondente, e per farlo controlla in ordine le directory
elencate nella lista sys.path. Una volta trovato il file, Python
lo importa, crea un module object (oggetto modulo), e lo salva nel
dizionario sys.modules. Se il modulo viene importato nuovamente in
un altro punto del programma, Python è in grado di recuperare il
modulo da sys.modules senza doverlo importare nuovamente. Inoltre,
i moduli .py vengono compilati in bytecode: un formato più
efficiente che viene salvato in file con estensione .pyc che a
loro volta vengono salvati in una cartella chiamata __pycache__.
Quando viene eseguito un programma che ha bisogno di importare un
modulo, se modulo.pyc esiste già ed è aggiornato, allora Python
importerà quello invece di ricompilare il modulo in bytecode ogni
volta.
La libreria standard e PyPI
Oltre al modulo math che abbiamo visto nei precedenti esempi,
Python include già dozzine di moduli che coprono la maggior parte
delle operazioni più comuni. Tutti questi moduli sono già presenti
in Python e possono essere importati direttamente senza dover
scaricare nulla. Alcuni tra i moduli più comunemente usati, divisi
per aree, sono:
elaborazione di testo: re (che fornisce supporto per le
espressioni regolari)
tipi di dato: datetime (per rappresentare date e
ore), collections (diversi tipi di oggetti
contenitori), enum (per enumerazioni)
moduli numerici e matematici: math (funzioni
matematiche), decimal (supporto per aritmetica a virgola
mobile), random (generazione di numeri pseudo-
casuali), statistics (funzioni statistiche)
programmazione funzionale: itertools (funzioni per lavorare con
gli iteratori), functools (funzioni di ordine superiore)
accesso a file e directory: pathlib (oggetti per la
rappresentazione e manipolazione di file e
directory), shutil (operazioni di alto livello sui file)
persistenza di dati: sqlite3 (interfaccia a database SQLite)
compressione e archiviazione di dati: zipfile (supporto per
archivi ZIP), tarfile (supporto per archivi tar), gzip (supporto
per file gzip)
84
formati di file: csv (supporto per file
CSV), configparser (supporto per file CFG/INI)
servizi generici del sistema operativo: os (diverse interfacce
del sistema operativo), io (strumenti per lavorare con file e
stream), time (funzioni relative al tempo), argparse (per
passare argomenti ricevuti dalla linea di
comando), logging (funzioni e classi per il logging)
esecuzione concorrente: subprocess (gestione di sotto-
processi), multiprocessing (parallelismo basato su
processi), threading (parallismo basato su thread)
comunicazione tra processi e networking: asyncio (I/O
asincrono), socket (interfaccia di rete di basso
livello), ssl (wrapper TLS/SSL per i socket)
gestione di formati di internet: json (supporto per file
JSON), email (supporto per email)
elaborazione di formati di markup: il package html (strumenti
per lavorare con HTML), il package xml (strumenti per lavorare
con xml)
protocolli internet: il package urllib (gestione di
URL), httplib (strumenti per lavorare con il protocollo
HTTP), ftplib (strumenti per lavorare con FTP)
internazionalizzazione: gettext (strumenti per supportare
linguaggi multipli)
interfacce grafiche: il package tkinter (interfaccia con Tcl/Tk)
tool per lo sviluppo: unittest (strumenti per testare il codice)
debugging e profiling: pdb (debugger per
Python), timeit (strumenti per misurare il tempo di esecuzione
di brevi pezzi di codice), cProfile (profiler per identificare
le parti più lente di un programma)
servizi di runtime: sys (funzioni e parametri di
sistema), contextlib (strumenti per lavorare con i context
manager)
Nella documentazione ufficiale possiamo trovare l’elenco completo
dei moduli della libreria standard di Python.
Se invece necessitiamo di moduli che non sono inclusi nella
libreria standard, possiamo consultare PyPI: il Python Package
Index. PyPI è un repository che include decine di migliaia di
moduli che possono essere scaricati e installati in modo molto
semplice usando un tool chiamato pip, che approfondiremo in una
apposita lezione successiva.
85
Lezione 22di 40
Creare nuovi moduli
Lezione 23di 40
Package
Oltre ai moduli, Python offre un ulteriore livello di
organizzazione: i package. Un package è una raccolta di moduli,
che in genere corrisponde alla directory che li contiene. Prima di
poter importare i moduli da una directory, è necessario aggiungere
un file vuoto chiamato __init__.py all’interno della directory.
Vediamo ad esempio la struttura che potrebbe avere un ipotetico
progetto Python:
ProgettoPython/
|- main.py
`- progetto/
|- __init__.py
|- core.py
|- utils.py
|- gui/
| |- __init__.py
| |- widgets.py
| `- windows.py
`- test/
|- __init__.py
|- test_core.py
|- test_utils.py
|- test_widgets.py
`- test_windows.py
88
La directory che contiene il nostro progetto si
chiama ProgettoPython e contiene un file main.py e il
package progetto. La directory progetto contiene un
file __init__.py che la rende un package importabile da Python,
oltre ai file core.py e utils.py e le sottocartelle gui e test. Le
sottocartelle contengono a loro volta due file __init__.py che le
rendono parte del package, e diversi altri file .py.
In questo caso la root (radice) del package è progetto, e per
poterla importare dobbiamo garantire che la cartella che lo
contiene (cioè ProgettoPython) sia inclusa in sys.path. Possiamo
farlo sia lanciando python3 direttamente dalla
cartella ProgettoPython (la cartella corrente viene aggiunta
automaticamente a sys.path), oppure aggiungendo il percorso
completo della directory (es. /home/ezio/ProgettoPython) alla
variabile di ambiente PYTHONPATH.
Possiamo ora identificare i package e sub-package all’intero
degli import, separandoli con un .. Ad esempio, per
importare utils possiamo scrivere import progetto.utils o from
progetto import utils. Se vogliamo importare una funzione
da utils, possiamo usare from progetto.utils import funzione. Se
vogliamo importare un widget da widgets, possiamo usare from
progetto.gui.widgets import widget.
Lezione 24di 40
La programmazione ad oggetti
In uno dei capitoli introduttivi di questa guida abbiamo visto che
Python è un linguaggio multi-paradigma, che supporta cioè sia la
programmazione procedurale (che fa uso delle funzioni), sia la
programmazione funzionale (includendo iteratori e generatori), sia
la programmazione ad oggetti (includendo funzionalità come
l’ereditarietà singola e multipla, l’overloading degli operatori,
e il duck typing).
In questo capitolo introdurremo i concetti della programmazione
orientata agli oggetti, e vedremo alcuni semplici esempi. Nei
capitoli successivi vedremo più in dettaglio come usare gli
oggetti in Python, la sintassi necessaria per crearli, e le
operazioni che supportano.
Gli oggetti
Mentre nella programmazione procedurale le funzioni (o procedure)
sono l’elemento organizzativo principale, nella programmazione ad
oggetti (anche conosciuta come OOP, ovvero object-Oriented
Programming) l’elemento organizzativo principale sono gli oggetti.
Nella programmazione procedurale, i dati e le funzioni sono
separate, e questo può creare una serie di problemi, tra cui:
è necessario gestire dati e funzioni separatamente;
89
è necessario importare le funzioni che vogliamo usare;
è necessario passare i dati alle funzioni;
è necessario verificare che i dati e le funzioni siano
compatibili;
è più difficile estendere e modificare le funzionalità;
il codice è più difficile da mantenere;
è più facile introdurre bug.
Nella programmazione ad oggetti, gli oggetti svolgono la funzione
di racchiudere in un’unica unità organizzativa sia i dati che il
comportamento. Questo ha diversi vantaggi:
dati e funzioni sono raggruppati;
è facile sapere quali operazioni possono essere eseguite sui
dati;
non è necessario importare funzioni per eseguire queste
operazioni;
non è necessario passare i dati alle funzioni;
le funzioni sono compatibili con i dati;
è più facile estendere e modificare le funzionalità;
il codice è più semplice da mantenere;
è più difficile introdurre bug.
Vediamo un semplice esempio: abbiamo la base e l’altezza di 100
diversi rettangoli e vogliamo sapere area e perimetro di ogni
rettangolo. Usando un approccio procedurale, possiamo risolvere il
problema creando due funzioni separate che accettano base e
altezza:
>>> # definiamo due funzioni per calcolare area e perimetro
>>> def calc_rectangle_area(base, height):
... """Calculate and return the area of a rectangle."""
... return base * height
...
>>> def calc_rectangle_perimeter(base, height):
... """Calculate and return the perimeter of a rectangle."""
... return (base + height) * 2
...
91
>>> myrect.calc_perimeter() # e anche il perimetro
16
92
rettangoli hanno una base, un’altezza, un’area e un perimetro, ma
la classe non si riferisce a nessun rettangolo in particolare.
Istanze
Le istanze sono oggetti creati a partire da una classe. Ad
esempio Rectangle(3, 5) ci restituisce un’istanza della
classe Rectangle che si riferisce a uno specifico rettangolo che
ha base 3 e altezza 5. Una classe può essere usata per creare
diverse istanze dello stesso tipo ma con attributi diversi, come i
100 diversi rettangoli che abbiamo visto nell’esempio precedente.
È possibile usare i metodi definiti dalla classe con ogni istanza,
semplicemente facendo istanza.metodo() (ad
esempio myrect.calc_area()).
Attributi
Gli attributi sono dei valori associati all’istanza, come ad
esempio la base e l’altezza del rettangolo. Gli attributi di ogni
istanza sono separati: ogni istanza di Rectangle ha una base e
un’altezza diversa. Per accedere a un attributo basta
fare istanza.attributo (ad esempio myrect.base).
Metodi
I metodi descrivono il comportamento dell’oggetto, sono simili
alle funzioni, e sono specifici per ogni classe. Ad esempio sia la
classe Rectangle che la classe Triangle possono definire un metodo
chiamato calc_area(), che ritornerà risultati diversi in base al
tipo dell’istanza. I metodi possono accedere altri attributi e
metodi dell’istanza: questo ci permette ad esempio di
chiamare myrect.calc_area() senza dover passare la base e
l’altezza esplicitamente. Per chiamare un metodo basta
fare istanza.metodo() (ad esempio myrect.calc_area()).
Ereditarietà
Un altro concetto importante della programmazione è
l’ereditarietà. L’ereditarietà ci permette di creare una nuova
classe a partire da una classe esistente e di estenderla o
modificarla.
Per esempio possiamo creare una classe Square che eredita dalla
classe Rectangle. Dato che i 4 lati di un quadrato hanno la stessa
lunghezza, non è più necessario richiedere base e altezza
separatamente, quindi nella classe Square possiamo modificare
l’inizializzazione in modo da richiedere la lunghezza di un
singolo lato. Così facendo possiamo definire una nuova classe che
invece di accettare e definire i due
attributi base e height definisce e accetta un singolo
attributo side. Dato che il quadrato è un tipo particolare di
rettangolo, i metodi per calcolare area e perimetro funzionano
senza modifiche e possiamo quindi
utilizzare calc_area() e calc_perimeter() ereditati
automaticamente dalla classe Rectangle senza doverli ridefinire.
È inoltre possibile definire gerarchie di classi, ad esempio si
può definire la clase Husky che eredita dalla classe Cane che
93
eredita dalla classe Mammifero che eredita dalla classe Animale.
Ognuna di queste classi può definire attributi e comportamenti
comuni a tutti gli oggetti di quella classe, e le sottoclassi
possono aggiungerne di nuovi.
Python supporta anche l’ereditarietà multipla: è possibile
definire nuovi classi che ereditano metodi e attributi da diverse
altre classi, combinandoli.
Nella prossima lezione vedremo esempi concreti di utilizzo
dell’ereditarietà.
Superclassi e sottoclassi
Se la classe Square eredita dalla classe Rectangle, possiamo dire
che Rectangle è la superclasse (o classe base – base class in
inglese) mentre Square è la sottoclasse (o subclasse).
L’operazione di creare una sottoclasse a partire da una classe
esistente è a volte chiamata subclassare.
Overloading degli operatori
In Python, le classi ci permettono anche di ridefinire il
comportamento degli operatori: questa operazione è
chiamata overloading degli operatori. È possibile definire metodi
speciali che vengono invocati automaticamente quando un operatore
viene usato con un’istanza della classe.
Ad esempio possiamo definire che myrect1 <
myrect2 ritorni True quando l’area di myrect1 è inferiore a quella
di myrect2, oppure possiamo definire che myrect1 + myrect2 ritorni
un nuova istanza di Rectangle creata dalla combinazione
di myrect1 e myrect2.
Quando usare la programmazione ad oggetti
Anche se la programmazione ad oggetti è uno strumento molto utile
e potente, non è la soluzione a tutti i problemi. Spesso creare
una semplice funzione è sufficiente e non è necessario definire
una classe.
In genere la programmazione ad oggetti può essere la soluzione
adatta se:
la classe che vogliamo creare rappresenta un oggetto
(es. Rectangle, Person, Student, Window, Widget, Connection,
ecc.);
vogliamo associare all’oggetto sia dati che comportamenti;
vogliamo creare diverse istanze della stessa classe.
La programmazione ad oggetti potrebbe non essere la soluzione
migliore se:
la classe che vogliamo creare non rappresenta un oggetto, ma ad
esempio un verbo (es. Find, Connect, ecc.);
vogliamo solo rappresentare dati (meglio usare una struttura
dati come list, dict, namedtuple, ecc.) o solo comportamenti
(meglio usare funzioni, eventualmente raggruppate in un modulo
separato);
94
vogliamo creare una sola istanza della stessa classe (meglio
usare un modulo per raggruppare dati e funzioni).
Ovviamente ci sono anche delle eccezioni (ad esempio il
pattern singleton, che definisce una classe che prevede una sola
istanza). Python è un linguaggio multiparadigma, ed è quindi
importante scegliere il paradigma che meglio si adatta alla
situazione.
Nella prossima lezione vedremo in dettaglio come definire e usare
classi in Python.
Lezione 25di 40
Classi in Python
Gli attributi di classe sono valori legati alla classe, che sono
comuni e accessibili da tutte le istanze. Per dichiarare attributi
di classe, esistono due modi: usando classe.attributo = valore o
usando attributo = valore nel corpo della dichiarazione di una
classe:
>>> # definiamo una classe Dog
>>> class Dog:
... # definiamo un attributo di classe
... scientific_name = 'Canis lupus familiaris'
... # definiamo un __init__ che accetta un nome
99
... def __init__(self, name):
... # creiamo un attributo di istanza per il nome
... self.name = name
...
>>> # creiamo due istanze di Dog
>>> rex = Dog('Rex')
>>> fido = Dog('Fido')
>>> # verifichiamo che ogni istanza ha un nome diverso
>>> rex.name
'Rex'
>>> fido.name
'Fido'
>>> # accediamo all'attributo di classe da Dog
>>> Dog.scientific_name
'Canis lupus familiaris'
>>> # accediamo all'attributo di classe dalle istanze
>>> rex.scientific_name
'Canis lupus familiaris'
>>> fido.scientific_name
'Canis lupus familiaris'
>>> # modifichiamo il valore dell'attributo di classe
>>> Dog.scientific_name = 'Canis lupus lupus'
>>> # verifichiamo il cambiamento dall'istanza
>>> rex.scientific_name
'Canis lupus lupus'
Lezione 26di 40
Ereditarietà
Lezione 27di 40
Metodi speciali
103
<Classe object ...>: il nome della classe seguita da
informazioni aggiuntive (ad esempio il valore di alcuni
attributi), il tutto racchiuso tra <...> (es. <Person object:
name='Ezio' surname='Melotti'>).
Classe(arg1, arg2, ..., argN): l’espressione usata per creare
l’istanza, in grado di dirci il nome della classe e il valore
degli attributi (es. Person('Ezio', 'Melotti')).
__bool__ e __len__
Il metodo speciale __bool__ può essere usato per definire se un
oggetto è vero o falso, mentre il metodo __len__ può ritornare la
lunghezza (o il numero di elementi) di un oggetto.
Se __bool__ non è definito, Python può usare il risultato
di __len__ per determinare se un oggetto è vero o falso (una
lunghezza diversa da 0 è considerata vera). Se anche __len__ non è
definito, l’oggetto è considerato vero.
>>> # definiamo una classe Team
>>> class Team:
... # definiamo un __init__ che assegna i membri all'istanza
... def __init__(self, members):
... self.members = members
... # definiamo un __bool__ che restituisce False se il
... # team ha 0 membri, altrimenti True
... def __bool__(self):
... return len(self.members) > 0
... # definiamo un __len__ che restituisce il numero di membri
... def __len__(self):
... return len(self.members)
... # definiamo un __repr__ che restituisce il tipo dell'oggetto
... # e i nomi dei membri del team
... def __repr__(self):
... names = ', '.join([p.name for p in self.members])
... return '<Team object [{}]>'.format(names)
...
>>> # creiamo un'istanza di Team con 3 membri
>>> t1 = Team([Person('Guido', 'van Rossum'),
... Person('Alex', 'Martelli'),
... Person('Ezio', 'Melotti'),])
>>> # verifichiamo il repr dell'oggetto
>>> t1
<Team object [Guido, Alex, Ezio]>
>>> # verifichiamo che il team ha 3 membri
>>> t1.members
[<Person object (Guido van Rossum)>,
<Person object (Alex Martelli)>,
<Person object (Ezio Melotti)>]
>>> # verifichiamo che la lunghezza del team è 3
>>> len(t1)
3
>>> # verifichiamo che questo team è considerato "vero"
>>> bool(t1)
True
>>>
104
>>> # creiamo un'altra istanza di Team con 0 membri
>>> t2 = Team([])
>>> # verifichiamo il repr dell'oggetto
>>> t2
<Team object []>
>>> # verifichiamo che il team ha 0 membri
>>> t2.members
[]
>>> # verifichiamo che la lunghezza del team è 0
>>> len(t2)
0
>>> # verifichiamo che questo team è considerato "falso"
>>> bool(t2)
False
Copy
Nell’esempio, abbiamo definito una classe Team che include una
lista di membri. Quando usiamo len(istanza), viene automaticamente
invocato il metodo Team.__len__(), che ci restituisce il numero di
membri nel team. Quando usiamo bool(istanza) o if istanza: ...,
viene invece invocato il metodo Team.__bool__(), che ci
restituisce True se il team ha almeno un membro, altrimenti False.
Lezione 28di 40
105
__radd__: quando eseguiamo valore + istanza, e il valore non
definisce un metodo __add__ compatibile con la nostra istanza,
viene eseguito il metodo istanza.__radd__(valore);
__iadd__: quando eseguiamo istanza += valore, viene
eseguito istanza.__iadd__(valore), permettendoci di modificare
l’istanza in place.
Vediamo alcuni esempi di overloading degli operatori:
>>> # definiamo una classe Team
>>> class Team:
... # definiamo un __init__ che assegna i membri all'istanza
... def __init__(self, members):
... self.members = members
... # definiamo un __repr__ che restituisce il tipo
dell'oggetto
... # e i nomi dei membri del team
... def __repr__(self):
... names = ', '.join([p.name for p in self.members])
... return '<Team object [{}]>'.format(names)
... # definiamo un __contains__ che restituisce True se un
membro
... # fa parte del team, altrimenti False
... def __contains__(self, other):
... return other in self.members
... # definiamo un __add__ che restituisce un nuovo team
creato
... # dall'aggiunta di una nuova persona o dall'unione di 2
team
... def __add__(self, other):
... if isinstance(other, Person):
... return Team(self.members + [other])
... elif isinstance(other, Team):
... return Team(self.members + other.members)
... else:
... raise TypeError("Can't add Team with
{!r}.".format(other))
... # definiamo un __radd__ che è uguale ad __add__, visto che
... # l'addizione è un'operazione commutativa
... __radd__ = __add__
... # definiamo un __iadd__ che modifica il team aggiungendo
una
... # nuova persona o i membri di un altro team al team
corrente
... def __iadd__(self, other):
... if isinstance(other, Person):
... self.members.append(other)
... return self
... elif isinstance(other, Team):
... self.members.extend(other.members)
... return self
... else:
106
... raise TypeError("Can't add {!r} to the
team.".format(other))
...
>>>
>>> # creiamo 4 istanze di Person
>>> guido = Person('Guido', 'van Rossum')
>>> tim = Person('Tim', 'Peters')
>>> alex = Person('Alex', 'Martelli')
>>> ezio = Person('Ezio', 'Melotti')
>>>
>>> # creiamo 2 team da 2 persone per team
>>> t1 = Team([guido, tim])
>>> t2 = Team([alex, ezio])
>>>
>>> # verifichiamo i membri dei 2 team
>>> t1
<Team object [Guido, Tim]>
>>> t2
<Team object [Alex, Ezio]>
>>>
>>> # verifichiamo l'overloading dell'operatore in
>>> guido in t1
True
>>> ezio in t1
False
>>> ezio not in t1
True
>>>
>>> # verifichiamo l'overloading dell'operatore + (__add__)
>>> # sommando un'istanza di Team con una di Person
>>> t1 + ezio
<Team object [Guido, Tim, Ezio]>
>>> # verifichiamo che l'operazione ha restituito
>>> # un nuovo team, e che t1 non è cambiato
>>> t1
<Team object [Guido, Tim]>
>>>
>>> # verifichiamo l'overloading dell'operatore + (__radd__)
>>> # sommando un'istanza di Person con una di Team
>>> ezio + t1
<Team object [Guido, Tim, Ezio]>
>>>
>>> # verifichiamo l'overloading dell'operatore + (__add__)
>>> # sommando due istanze di Team
>>> t1 + t2
<Team object [Guido, Tim, Alex, Ezio]>
>>> t2 + t1
<Team object [Alex, Ezio, Guido, Tim]>
>>>
>>> # verifichiamo che t1 contiene 2 membri
>>> t1
<Team object [Guido, Tim]>
107
>>> # verifichiamo l'overloading dell'operatore += (__iadd__)
>>> # aggiungendo un'istanza di Person al Team t1
>>> t1 += ezio
>>> # verifichiamo che t1 è stato modificato
>>> t1
<Team object [Guido, Tim, Ezio]>
>>>
>>> # creiamo altre 2 istanze di Team
>>> t3 = Team([alex, tim])
>>> t4 = Team([guido, ezio])
>>> # verifichiamo che t3 contiene 2 membri
>>> t3
<Team object [Alex, Tim]>
>>> # verifichiamo l'overloading dell'operatore += (__iadd__)
>>> # aggiungendo un'istanza di Team al Team t3
>>> t3 += t4
>>> # verifichiamo che t3 è stato modificato
>>> t3
<Team object [Alex, Tim, Guido, Ezio]>
>>>
>>> # verifichiamo che aggiungere un tipo incompatibile
>>> # ci restituisce un TypeError
>>> t3 + 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in __add__
TypeError: Can't add Team with 5.
Operator
e Descrizione Metodi speciali
+ addizione __add__, __radd__, __iadd__
– sottrazione __sub__, __rsub__, __isub__
* moltiplicazion __mul__, __rmul__, __imul__
108
e
/ divisione __truediv__, __rtruediv__, __itruediv__
divisione __floordiv__, __rfloordiv__, __ifloordiv_
// intera _
modulo (resto
della
% divisione) __mod__, __rmod__, __imod__
Ogni operatore di confronto ha solo un corrispondente metodo
speciale:
109
Esistono anche metodi speciali per determinare il comportamento di
un’oggetto durante l’accesso, l’assegnamento, e la rimozione di
elementi o attributi:
Lezione 29di 40
110
Cross-platform_ totalmente portabili, conferiscono (quasi)
sempre lo stesso aspetto a tutte le applicazioni,
indipendentemente dal sistema operativo che le esegue;
Platform-specific: utilizzano elementi visuali forniti dal
particolare sistema operativo che esegue l’interfaccia.
Tra le caratteristiche principali che andrebbero valutate quando
si intende scegliere il framework più adatto alla nostra
applicazione Python, bisognerebbe valutare:
Widget: è importante conoscere quali strumenti visuali mette a
disposizione il framework (es. bottoni, menu, canvas, ecc…) per
comprendere fin da subito se è ciò che fa per la nostra
applicazione;
Licenza: il tipo di licenza offerta dal framework che intendiamo
utilizzare è fondamentale, specialmente nel caso di applicazioni
di natura commerciali;
IDE per lo sviluppo: l’esistenza di uno strumento IDE risulta
fondamentale per abbattere i tempi di sviluppo e test. Quando
possibile, dovremo sempre scegliere un framework con una buona
IDE a supporto del processo di costruzione dell’interfaccia;
Supporto da parte del produttore: molti dei framework esistenti
sul web sono caduti in disuso, e spesso la ragione è il mancato
supporto da parte del produttore, la mancanza di aggiornamenti o
la mancanza di un IDE.
Tra le decine e decine di frameworks per lo sviluppo di GUI in
Python, vedremo adesso quelli che sono maggiormente
utilizzati/supportati dai produttori. I framework con scarso
supporto o caduti in disuso non verranno trattati in questa sede.
Tra quelli osservati in questa breve panoramica, approfondiremo
(nelle lezioni successive) quelli più utilizzati e meglio
supportati.
Framework cross-platform
I framework cross-platform hanno la caratteristica di essere
portabili su diversi sistemi operativi, consentendo agli
sviluppatori di scrivere codice indipendente dalle librerie
grafiche del sistema in esecuzione. Nella maggior parte dei casi,
ciò implica che l’aspetto finale dell’applicazione rimarrà lo
stesso, indipendentemente dal sistema operativo in esecuzione. I
framework cross-platform che vedremo nella seguente tabella, in
alcuni casi, possono anche appoggiarsi su librerie grafiche
native: questo è un vantaggio in termini di efficienza
dell’applicazione grafica, che sfrutterà appieno le capacità del
sistema. D’altro canto, però, l’aspetto finale delle applicazioni
cambierà (seppur in modo limitato) a seconda del sistema operativo
su cui esse saranno eseguite.
Home
Nome page Descrizione IDE consigliato
Framework platform-specific
I framework platform-specific non sono molto diffusi, in quanto le
applicazioni risultanti sono utilizzabili solo una categoria di
sistemi operativi. Nella seguente tabella vedremo le
caratteristiche di due framework che stanno comunque ottenendo una
discreta diffusione nell’ambito dei sistemi Windows ed Android:
Home
Nome page Descrizione IDE consigliato
Lezione 30di 40
Tkinter
elif version_info.major == 3:
# Python 3.x
from tkinter import *
from tkinter.ttk import *
Widget
I widget di Tkinter sono numerosi ed elencati nella seguente
tabella, che li raggruppa in base alla categoria:
Categoria Nome Descrizione
Base Optionmenu
Simile al precedente, ma trattasi di menu
116
di tipo “popup”
117
I componenti di ttk sono in tutto 17, di cui 11 già esistenti in
Tkinter. Rispetto ai componenti della vecchia versione, avremo
quindi a disposizione 6 nuovi componenti, qui di seguito elencati:
Layout manager
Al fine di posizionare i widget all’interno delle finestre della
GUI, dovremo utilizzare un layout manager. Tkinter ne mette a
disposizione tre:
pack: consente di elencare in sequenza gli oggetti da disporre
nella nostra GUI. L’ordine di visualizzazione è identico
all’ordine con cui vengono registrati tramite
l’istruzione pack();
grid: consente di posizionare gli oggetti all’interno di una
griglia 2-D. Gli oggetti vengono posizionati tramite l’utilizzo
dell’istruzione grid(), indicando numero di riga e di colonna;
place: consente di indicare la posizione assoluta/relativa degli
oggetti all’interno della GUI. Utilizza l’istruzione place().
L’unica avvertenza da tenere a mente è che i layout non vanno MAI
mescolati nella stessa applicazione. Il più semplice e rapido da
utilizzare è il layout “grid”, mentre il più preciso è il layout
“place”.
118
PAGE: un IDE per la creazione di interfacce grafiche con Tk/Tcl
PAGE (Python Automatic GUI Generator)
nerator) è una IDE Drag and Drop per
la semplificazione del
l processo di creazione delle interfacce
grafiche in Python. Compatibile con Python 2.7 e versioni
successive, fornisce il supporto per i widget classici e tematici.
È disponibile tramite licenza GNU.
Per la sua installazione sarà sufficiente scaricarlo
da Sourceforge.
Utilizzando Ubuntu 16.04, scarichiamo l’archivio “.tgz” ed
estraiamone il contenuto in una cartella denominata “page”.
Dopodichè aprendo il terminale, dalla directory scompattat
scompattata
digitiamo il comando per la configurazione ./configure.
./configure
A questo punto, digitando il comando ./page,
, si aprirà l’IDE:
Figura 2. Interfaccia grafica di PAGE (click per ingrandire)
119
Figura 3. Esecuzione dell’interfaccia generata da PAGE (click per
ingrandire)
Lezione 31di 40
PyQt
120
capaci di eseguire le Qt, inclusi Windows, OS
X, Linux, iOS e Android
Android.. La documentazione ufficiale di PyQt è
disponibile sul sito della RiverBank.
Moduli
Le classi messe a disposizione da PyQt5 sono oltre 1000,
raggruppate in circa 50 moduli, che rendono questa libreria molto
più versatile e potente di Tkinter.
. Nella seguente tabella
121
riassumeremo i principali moduli (con le relative funzionalità)
per iniziare a sviluppare le prime interfacce:
122
Figura 2. Tipologie di licenze per le librerie Qt (click per
ingrandire)
123
Il sistema è veramente semplice da utilizzare. Sulla sinistra,
dalla Widget box,
, potremo scegliere il tipo di layout con il quale
posizionare gli oggetti sull’interfaccia. Inoltre, avremo a
disposizione la lista dei widget posizionabili (non interamente
visibili in figura). Sulla destra, avremo a disposizione
il property editor,
, che consentirà di personalizzare i widget
inseriti.
Esempio: realizzare un’interfaccia di login
Lanciamo QtDesigner, e clicchiamo su File -> New ->
> Main Window:
Window
Figura 4. Finestra creata con Qt Designer (click per ingrandire)
125
Figura 7. Aspetto dell’interfaccia eseguita con Python (click per
ingrandire)
A questo punto saremo liberi di utilizzare il file Python appena
generato ed implementare la logica di controllo della nostra
interfaccia.
Lezione 32di 40
Kivy
(main.py)
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class ScreenManagement(ScreenManager):
pass
class LoginPage(Screen):
def verify(self):
user=self.ids["login"].text
pwd=self.ids["password"].text
if user== "user" and pwd== "passwd":
print('login successful')
class LoginApp(App):
def builder(self):
return kv_file
kv_file = Builder.load_file('login.kv')
if __name__ == '__main__':
LoginApp().run()
126
(Login.kv)
ScreenManagement:
LoginPage:
<LoginPage>:
BoxLayout:
orientation: 'vertical'
padding: [10,50,10,50]
spacing: 20
Label:
text: 'Login'
font_size: 18
halign: 'left'
TextInput:
id: login
multiline:True
font_size: 28
readonly: False
text: 'user'
on_text: root.verify()
Label:
text: 'Password'
halign: 'left'
font_size: 18
TextInput:
id: password
multiline:False
password:True
font_size: 28
text: 'passwd'
Button:
text: 'Connect'
font_size: 24
127
on_press: root.verify()
class PrimaApp(App):
def build(self):
):
return Button
Button(text='Hello World')
PrimaApp().run()
Installazione su Windows
Per installare Kivy su Windows, apriamo il terminale e digitiamo:
python -m pip install --
--upgrade pip wheel setuptools
Installazione su Linux
Per installare Kivy su Linux, supponendo di avere a
disposizione apt,
, aggiungiamo il seguente repository al nostro
sistema:
sudo add-apt-repository
repository ppa:kivy-team/kivy
130
Figura 3. Architettura di Kivy (fonte: Kivy architecture)
architecture (click
per ingrandire)
Il livello più basso deve essere pensato come un “ammasso” non ben
organizzato di tutte le funzionalità che Kivy intende fornire.
Tale livello è strettamente dipendente dal sistema operativo in
uso e per tale motivo viene reso trasparente all’utente finale
tramite il
l successivo livello di astrazione.
Il livello intermedio, scritto in C, è ancora dipendente dal
sistema operativo e agisce da “collante” tra il livello più basso
e i moduli Python del livello più alto. Fondamentalmente non fa
altro che riorganizzare le fu
funzionalità
nzionalità dei moduli di basso
livello in un insieme ordinato e le espone secondo un’interfaccia
C standard.
Il livello più alto, scritto interamente in Python, è l’insieme di
librerie che troveremo identiche su ogni piattaforma. In
particolare, troveremo a disposizione i seguenti moduli:
Clock:
: un modulo che mette a disposizione timers per la
sincronizzazione;
Cache:
: un modulo progettato per incrementare le prestazioni
delle nostre app, che consente di velocizzare l’accesso a dati
acceduti frequentemente;
131
Gesture Detection: un riconoscitore di gesti da utilizzare su
dispotivi touch. Può essere utilizzato anche per riconoscere
gesti personalizzati;
Kivy Language: un linguaggio per la descrizione delle interfacce
grafiche che facilita il posizionamento dei widget nella nostra
app;
Properties: proprietà da utilizzare per ottimizzare la veste
grafica della nostra app.
UIX
Tale modulo contiene le classi per la creazione di applicazioni
desktop/mobile, che fondamentalmente rappresenta i contenuti della
libreria Tkinter, mettendo a disposizione elementi come i bottoni,
file browser, pannelli e quant’altro. In Kivy, le funzionalità di
“contenitore” vengono svolte dai moduli di layout. I layout
disponibili sono cinque: box, grid, stack, anchor e float.
I layout box, grid e stack sono principalmenti utilizzati per
interfacce grafiche semplici composte da bottoni e canvas. I
layout anchor e float consentono un posizionamento più preciso e
sono utilizzati tipicamente nelle applicazioni desktop. Impostare
il layout di un bottone, ad esempio, consta di tre semplici
istruzioni:
layout = BoxLayout(padding=5)
button = Button(text=’Bottone di prova’)
layout.add_widget(button)
133
Il codice sorgente di questo esempio è allegato a questa lezione,
ed è disponibile qui.(Vai sul sito di HTML.it)
(main.py)
kv_file = Builder.load_file('login.kv')
if __name__ == '__main__':
LoginApp().run()
(Login.kv)
ScreenManagement:
LoginPage:
<LoginPage>:
BoxLayout:
orientation: 'vertical'
padding: [10,50,10,50]
spacing: 20
134
Label:
text: 'Login'
font_size: 18
halign: 'left'
TextInput:
id: login
multiline:True
font_size: 28
readonly: False
text: 'user'
on_text: root.verify()
Label:
text: 'Password'
halign: 'left'
font_size: 18
TextInput:
id: password
multiline:False
password:True
font_size: 28
text: 'passwd'
Button:
text: 'Connect'
font_size: 24
on_press: root.verify()
135
Lezione 33di 40
Python e Java
C:jython>jython
Jython 2.0 on java1.2 (JIT: symcjit)
Type “copyright”, “credits” or “license” for more information.
>>>
Python e i database
137
Voglio costruire un programma che mostra tutti i record della
tabella “elenco_articoli”. Ecco il semplice listato che risolve
questo problema:
import odbc
try:
s = odbc.odbc(‘articoli’) # mi collego al DSN
cur = s.cursor()
cur.execute(‘select * from elenco_articoli’)
rec = cur.fetchall()
Codice — Descrizione
233412 — matita
567543 — quaderno
533232 — gomma
Analizzando il codice si possono fare le seguenti osservazioni:
la variabile “s” rappresenta l’oggetto database. La variabile è
stata inizializzata utilizzando il modulo odbc.
138
la variabile “cur” rappresenta un cursore sul database. Quindi
attraverso questo cursore è possibile navigare nella struttura
del database.
attraverso il metodo “execute” è possibile eseguire una
richiesta SQL.
Il metodo “fetchall()” restituisce una lista contenente tutti i
record risultanti dalla query SQL. Ogni elemento della lista
“rec” è a sua volta una lista con i valori dei campi. In questo
caso i campi sono due:
Lezione 35di 40
Python e cgi
Nota: lezione in aggiornamento
Programmazione CGI
Python ha avuto un discreto successo nello sviluppo di
applicazioni web. In particolare viene utilizzato come linguaggio
di script richiamato da un server web attraverso la metodologia
CGI (common gateway interface). Il meccanismo CGI funziona nel
seguente modo:
è necessario avere installato sul proprio computer i seguenti
programmi:
Un server web, come Apache o Internet Information Server. Con
questo tipo di programma è possibile costruire un sito internet
sul vostro computer. Il server web, infatti, risponde alle
chiamate in protocollo HTTP e fornisce come risposta dei files
HTML contenuti nel vostro Hard Disk.
L’interprete python.
Potreste decidere di richiamare un programma python attraverso il
web accedendo al sito internet creato dal server web.
Per fare questo è necessario installare un modulo (CGI) che
139
permetta di invocare l’interprete python quando l’url presentata
al server web ne richieda l’esecuzione.
I moduli CGI si trovano in rete, ne esistono per tutti i
principali web server. Ad esempio per Apachee esiste
il mod_python, sia per linux che per Microsoft Windows.
Una volta installato mod_python, diventa semplice richiamare un
programma python.
Ad esempio: supponiamo che sulla nostra macchina sia stato
installato un web server che risponde alle richieste http con il
dominio “http://www.mio.it”.
Per invocare il programma è sufficiente digitare sul web la
seguente url: “http://www.mio.it/mio_programma.py”. In questo caso
è stato richiesto il programma python “mio_programma.py” contenuto
nella root directory del server web.
Vediamo ora un semplice programma CGI in python:
def main():
print ‘Content-type: text/html’
print
print ‘<HTML><HEAD><TITLE> Ciao,
mondo!</TITLE><BODY>” print ‘Ciao, mondo!’
print ‘</BODY></HTML>’
if (__name__ == ‘__main__’):
main()
Come si può osservare dal codice, il programma non fa altro che
scrivere in output un file html. Infatti il CGI cattura tutto
l’output del programma python e lo manda come risposta sul web.
L’utente che ha richiesto l’esecuzione del programma otterra’ in
risposta il seguente documento html:
Lezione 36di 40
Multithreading
141
uno scopo comune, eventualmente condividendo le stesse risorse
computazionali, nonché gli stessi dati.
I processori dei computer più recenti sono generalmente multi-
core, offrendo quindi la possibilità di eseguire più operazioni
parallele, sfruttando al meglio le risorse computazionali del
calcolatore. Sebbene ciò sia vero, la programmazione concorrente
nasconde spesso alcune difficoltà non banali, che vanno gestite
opportunamente per evitare errori come deadlock o problemi
di sincronizzazione.
In questa lezione vedremo le principali opzioni offerte da Python
per programmare con i thread. A tale scopo, la lezione si
concentrerà sull’uso del modulo threading, mentre il
modulo _thread (successore di quello che su Python 2.x era il
modulo thread) non sarà trattato in quanto considerato deprecato
dalla community di Python.
Creazione ed avvio di un thread
La creazione di un thread con Python 3 necessita della definizione
di una classe, che erediti dalla classe Thread. Quest’ultima è
inclusa nel modulo threading, che va quindi importato. La classe
che definiremo (rappresentante dunque il nostro thread) dovrà
rispettare una precisa struttura: dovremo innanzitutto definire il
metodo __init__, ma soprattutto dovremo sovrascrivere il
metodo run.
Per capire meglio come procedere, vediamo un semplice esempio
pratico:
from threading import Thread
import time
class IlMioThread (Thread):
def __init__(self, nome, durata):
Thread.__init__(self)
self.nome = nome
self.durata = durata
def run(self):
print ("Thread '" + self.name + "' avviato")
time.sleep(self.durata)
print ("Thread '" + self.name + "' terminato")
142
thread2 = IlMioThread("Thread#2", randint(1,100))
thread3 = IlMioThread("Thread#3", randint(1,100))
# Avvio dei thread
thread1.start()
thread2.start()
thread3.start()
# Join
thread1.join()
thread2.join()
thread3.join()
# Fine dello script
print("Fine")
143
threading.Thread.__init__(self)
self.nome = nome
self.durata = durata
def run(self):
print ("Thread '" + self.name + "' avviato")
# Acquisizione del lock
threadLock.acquire()
time.sleep(self.durata)
print ("Thread '" + self.name + "' terminato")
# Rilascio del lock
threadLock.release()
# Creazione dei thread
thread1 = IlMioThread("Thread#1", randint(1,100))
thread2 = IlMioThread("Thread#2", randint(1,100))
thread3 = IlMioThread("Thread#3", randint(1,100))
# Avvio dei thread
thread1.start()
thread2.start()
thread3.start()
# Join
thread1.join()
thread2.join()
thread3.join()
# Fine dello script
print("Fine")
144
Lezione 37di 40
146
Si noti che, così facendo, non abbiamo specificato nessuna
informazione relativa all’indentazione da utilizzare nel file
generato come output. Possiamo aggiungere questa ulteriore
possibilità sfruttando il parametro opzionale indent, che
specifica il numero di spazi di usare per l’indentazione:
with open("output.json", "w") as outfile:
json.dump(data, outfile, indent=4) #indentazione con 4
spazi
Lezione 38di 40
File XML: leggere e scrivere
149
<rubrica>
<persona id="1">
<nome>Vito</nome>
<cognome>Gentile</cognome>
</persona>
</rubrica>
150
Lezione 39di 40
Future statement
Come si vede, è basato una semplice import per cambiare (di fatto)
la sintassi di Python 2.x. È importante notare che questa sintassi
non ha soltanto definito una nuova funzione print, ma ha
anche eliminato lo statement print. Ad esempio, il codice seguente
causerebbe un errore di sintassi:
from __future__ import print_function
print "Ciao!" #errore di sintassi
151
operandi può cambiare radicalmente il risultato della divisione.
Facciamo un esempio:
3 / 2 #su Python 2.x, il risultato è 1. Su Python 3, il
risultato è 1.5
3.0 / 2 #sia su Python 2.x che su Python 3, il risultato è 1.5
3 // 2 #sia su Python 2.x che su Python 3, il risultato è 1
Altre funzionalità
Oltre alle due funzionalità relative alla funzione print ed alle
divisioni tra interi, i future statements permettono di importare
molte altre caratteristiche della versione 3 del linguaggio Python
anche su Python 2.x. Una di queste è il costrutto with, già visto
nella lezione sui file, che può essere importato come segue:
from __future__ import with_statement
with open('text.txt', 'r') as f: #sintassi di Python 3 utilizzata
su Python 2.x
print f.read() #sintassi standard di Python 2.x
152
Lezione 40di 40
Aggiornare pip
È anche consigliato verificare che la versione di pip sia sempre
aggiornata. Per aggiornarlo, possiamo usare il comando python3 -m
pip install -U pip, o più semplicemente pip install -U pip.
Usare pip
pip supporta una serie di comandi che ci permettono, tra le altre
cose,
di cercare, scaricare, installare, aggiornare e rimuovere package.
Vediamo in dettaglio il funzionamento dei comandi più comuni.
153
Cercare package
Per cercare package nel Python Package Index, possiamo usare il
comando python3 -m pip search KEYWORD. Ad esempio, se volessimo
cercare il package BeautifulSoup, possiamo eseguire il comando
seguente, osservando il relativo output:
$ python3 -m pip search beautifulsoup
beautifulscraper (1.1.0)
- Python web-scraping library that wraps urllib2 and
BeautifulSoup.
scrapy-beautifulsoup (0.0.2)
- Simple Scrapy middleware to process non-well-formed HTML with
BeautifulSoup
ipython-beautifulsoup (0.3)
- Custom rendering of beautifulsoup objects in IPython notebook
and qtconsole
django-beautifulsoup-test (1.1.3)
- TestCase class for using BeautifulSoup with Django tests
BeautifulSoup (3.2.1)
- HTML/XML parser for quick-turnaround applications like screen-
scraping.
beautifulsoup4-slurp (0.0.2)
- Slurp packages Beautifulsoup4 into command line.
beautifulsoup4 (4.6.0)
- Screen-scraping library
INSTALLED: 4.6.0 (latest)
beautifulsoupselect (0.2)
- Simple wrapper to integrate BeautifulSoup and soupselect.py in
a single package
collective.soupstrainer (2.0)
- Clean up HTML using BeautifulSoup and filter rules.
Detextile (0.0.3)
- Convert HTML to Textile syntax using BeautifulSoup.
spider-egg (0.1.0)
- a template for python crawler with requests and beautifulsoup
ElementSoup (rev452)
- ElementTree wrapper for BeautifulSoup HTML parser
...
È inoltre possibile usare gli operatori <, <=, >, >= per
specificare versioni massime e minime. Queste espressioni si
possono anche combinare usando la virgola (,), permettendoci di
specificare sia una versione minima che una massima. Ad esempio,
il seguente comando installerà la versione più recente
di requests 2.16.x, ma non installerà la versione 2.17 o
successive, anche se sono disponibili:
$ python3 -m pip install 'requests>=2.16,<2.17'
Collecting requests<2.17,>=2.16
Using cached requests-2.16.5-py2.py3-none-any.whl
155
Collecting chardet<3.1.0,>=3.0.2 (from requests<2.17,>=2.16)
Using cached chardet-3.0.4-py2.py3-none-any.whl
Collecting urllib3<1.22,>=1.21.1 (from requests<2.17,>=2.16)
Using cached urllib3-1.21.1-py2.py3-none-any.whl
Collecting idna<2.6,>=2.5 (from requests<2.17,>=2.16)
Using cached idna-2.5-py2.py3-none-any.whl
Collecting certifi>=2017.4.17 (from requests<2.17,>=2.16)
Using cached certifi-2017.7.27.1-py2.py3-none-any.whl
Installing collected packages: chardet, urllib3, idna, certifi,
requests
Successfully installed certifi-2017.7.27.1 chardet-3.0.4 idna-2.6
requests-2.16.5 urllib3-1.22
Disinstallare package
Per rimuovere un package che abbiamo installato in precedenza,
basta eseguire python3 -m pip uninstall PACKAGE. Ad esempio, se
volessimo rimuovere il package requests, possiamo eseguire:
$ python3 -m pip uninstall requests
Uninstalling requests-2.18.4:
.../lib/python3.6/site-packages/requests-2.18.4.dist-
info/DESCRIPTION.rst
.../lib/python3.6/site-packages/requests-2.18.4.dist-
info/INSTALLER
.../lib/python3.6/site-packages/requests-2.18.4.dist-
info/METADATA
.../lib/python3.6/site-packages/requests-2.18.4.dist-info/RECORD
.../lib/python3.6/site-packages/requests-2.18.4.dist-info/WHEEL
.../lib/python3.6/site-packages/requests-2.18.4.dist-
info/metadata.json
.../lib/python3.6/site-packages/requests-2.18.4.dist-
info/top_level.txt
157
.../lib/python3.6/site-packages/requests/__init__.py
...
Proceed (y/n)? y
Successfully uninstalled requests-2.18.4
158
INDICE Pag.
10 Tuple ………………………………………………………………………………………………………………………….. 30
12 Dizionari …………………………………………………………………………………………………………………….. 35
21 Moduli …………………………………………………………………………………………………………………………. 81
23 Package ……………………………………………………………………………………………………………………… 88
159
La programmazione a oggetti con Pytho
30 Tkinter …………………………………………………………………………………………………………………………115
31 PyQt …………………………………………………………………………………………………………………………..120
32 Kivy …………………………………………………………………………………………………………………………….126
Approfondimenti
160