3 SOMMARIO INTRODUZIONE E UN PO’ DI STORIA ........................................................................................................................... 3 DIFFERENZE TRA PYTHON 2 E PYTHON 3 .................................................................................................................................. 4 PERCHÉ USARE PYTHON ............................................................................................................................................. 5 PYTHON: I PUNTI DI FORZA .................................................................................................................................................... 5 COSA SI PUÒ FARE CON PYTHON ............................................................................................................................................. 6 CHI USA PYTHON ................................................................................................................................................................. 6 INSTALLARE PYTHON .................................................................................................................................................. 7 INSTALLARE PYTHON SU WINDOWS ........................................................................................................................................ 7 INSTALLARE PYTHON SU LINUX ............................................................................................................................................... 8 INSTALLARE PYTHON SU MAC ................................................................................................................................................ 9 L’INTERPRETE PYTHON E L’IDLE .................................................................................................................................10 ESECUZIONE A RIGA DI COMANDO ......................................................................................................................................... 10 MANDARE IN ESECUZIONE FILE .PY ........................................................................................................................................ 11 USARE L’IDLE .................................................................................................................................................................. 11 LE FUNZIONI PRINT E INPUT ......................................................................................................................................14 PRINT ............................................................................................................................................................................. 14 INPUT ............................................................................................................................................................................. 14 INDENTAZIONE E BLOCCHI DI CODICE IN PYTHON .....................................................................................................16 INDENTAZIONE E BLOCCHI DI CODICE ..................................................................................................................................... 16 VARIABILI E COMMENTI IN PYTHON ..........................................................................................................................18 I COMMENTI IN PYTHON ..................................................................................................................................................... 19 NUMERI E OPERATORI LOGICI IN PYTHON .................................................................................................................20 OPERATORI LOGICI IN PYTHON ............................................................................................................................................. 21 STRINGHE IN PYTHON ................................................................................................................................................22 SLICING E SOTTO-STRINGHE ................................................................................................................................................. 22 METODI PER LE STRINGHE ................................................................................................................................................... 23 COSTRUIRE LE STRINGHE ..................................................................................................................................................... 24 LISTE ..........................................................................................................................................................................25 METODI PER LE LISTE.......................................................................................................................................................... 26 DIZIONARI .................................................................................................................................................................29 INSERIMENTO, MODIFICA E CANCELLAZIONE ............................................................................................................................ 30 METODI PER I DIZIONARI ..................................................................................................................................................... 30 ORDINAMENTO DELLE CHIAVI............................................................................................................................................... 31 TUPLE ........................................................................................................................................................................33 TIPI MUTABILI ................................................................................................................................................................... 33 USARE LE TUPLE ................................................................................................................................................................ 33 A COSA SERVONO LE TUPLE? ................................................................................................................................................ 35 CONVERSIONE LISTE-TUPLE, TUPLE-LISTE ................................................................................................................................ 35 I FILE IN PYTHON .......................................................................................................................................................36 MODALITÀ DI APERTURA DI OPEN ......................................................................................................................................... 37 MUOVERSI ALL’INTERNO DEL FILE ......................................................................................................................................... 37 CICLI E ISTRUZIONI CONDIZIONALI .............................................................................................................................39 IF-ELIF-ELSE ...................................................................................................................................................................... 39 CICLO WHILE .................................................................................................................................................................... 40 CICLO FOR........................................................................................................................................................................ 41 1 FORZARE L’USCITA DAL CICLO ............................................................................................................................................... 42 L’ISTRUZIONE PASS ............................................................................................................................................................ 43 COMPREHENSION PER LISTE E DIZIONARI..................................................................................................................44 ESEMPI DI COMPREHENSION ................................................................................................................................................ 44 FUNZIONI ..................................................................................................................................................................46 DEFINIRE UNA FUNZIONE IN PYTHON ..................................................................................................................................... 46 SCOPE ............................................................................................................................................................................. 46 PASSAGGIO DEI PARAMETRI ................................................................................................................................................. 47 PARAMETRI FACOLTATIVI .................................................................................................................................................... 48 ALTRI ESEMPI.................................................................................................................................................................... 49 MODULI .....................................................................................................................................................................50 SCRIVERE E IMPORTARE UN MODULO..................................................................................................................................... 50 COMPILAZIONE OTTIMIZZATA DEI MODULI .............................................................................................................................. 51 I MODULI STANDARD .......................................................................................................................................................... 51 NAMESPACES ................................................................................................................................................................... 52 GESTIONE DELLE ECCEZIONI .......................................................................................................................................54 CLASSI E CENNI DI PROGRAMMAZIONE AD OGGETTI ................................................................................................56 CLASSI IN PYTHON .....................................................................................................................................................61 APPENDICE ................................................................................................................................................................67 COLORCONSOLE ................................................................................................................................................................ 67 LA GRAFICA ...................................................................................................................................................................... 68 2 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 sono svariati: Python è attualmente utilizzato per realizzare interfacce GUI, per lavorare con i database, per lo sviluppo di applicazioni Web e desktop, per realizzare giochi, grafica 3D, etc. 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 delle proprie applicazioni. Nota: Questa è la riedizione della guida Python di Stefano Riccio, revisionata e aggiornata da 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. Qualche anno dopo (fine anni ottanta) Guido Van Rossum ha avuto una serie di idee mirate al miglioramento di ABC, pertanto si mise a lavorare allo sviluppo di un nuovo linguaggio: Python. 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 per il progetto, essendo leggermente irriverente (e 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 arrivato alla versione 1.6. Qualche tempo dopo viene rilasciata la versione 2.0, una delle principali aggiunte in questa versione è quella delle “list comprehension“. Nel 2001 viene rilasciato Python 2.1 e ridefinita la licenza con “Python Software Foundation License”. Python 2.2 fu considerato un “rilascio puliza”, ci sono molte novità ma sicuramente la principale è 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 ha rotto la compatibilità con le vecchie versioni 2.X. Vediamo di seguito alcuni di questi importanti cambiamenti. 3 Differenze tra Python 2 e Python 3 Alcune delle novità introdotte con Python 3.X hanno segnato una discontinuità nella compatibilità con le versioni precedenti (2.x). Ci sono cambiamenti facili da segnalare, come la modifica dell’istruzione print in funzione print, altri invece sono molto delicati come ad esempio il cambiamento del tipo stringa in tipo byte (sequenza di byte). Ecco una tabella che mostra i principali cambiamenti tra le due versioni: Python 2.X Python 3.X Print "x" (senza parentesi) X <> Y Print("x") Long Int raw_input() input() try...except Exception, e try...except Exception as e Raise Exception, "Errore" Raise Exception("Errore") standardError class Exception class import mod from . import mod my_dict.keys() ritorna liste unicode("ciao") my_dict.keys() ritorna viste dinamiche str("ciao") >>>k={1:"andrea", 5:"giuseppe"} >>>1 in k True import httplib >>>k={1:"andrea", 5:"giuseppe"} >>>k.has_key(1) True import http.client import constant from . Import constant types.UnicodeType Str types.StringType bytes types.IntType int types.LongType int X != Y È possibile trovare in dettaglio tutte le novità della versione 3.X dal sito ufficiale, nei prossimi capitoli della guida vedremo più nel dettaglio e nella pratica alcuni di questi cambiamenti. 4 Perché usare Python Oggi esistono numerosi linguaggi di programmazione, ma cosa spinge ad usare Python, cosa c’è di particolare in questo linguaggio? Per rendercene conto, in questa lezione esaminiamo alcuni dei punti di forza di Python ed alcune delle sue applicazioni principali. Python: i punti di forza free Python è completamente gratuito ed è possibile usarlo e distribuirlo senza restrizioni di copyright. Spesso alla parola “free” è associato il concetto di “software non supportato”, ma questo non è assolutamente vero nel caso di Python: è sufficiente consultare periodicamente il sito ufficiale per rendersene conto. orientato agli oggetti Python è un linguaggio orientato agli oggetti, supporta nozioni avanzate di polimorfismo, ereditarietà, operatori di overloading il tutto con una semplice sintassi. 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 Nokia e Android, questo perché è interpretato, quindi lo stesso codice può essere eseguito su qualsiasi piattaforma purché abbia l’interprete Python installato. facile da usare Chi ha programmato in altri linguaggi di programmazione troverà Python semplice da usare, questo grazie alle semplici regole di sintassi e al fatto che generale si opera ad un alto livello di astrazione. ricco di librerie La dotazione standard offre numerose librerie alle quali si aggiungono moduli di terze parti che crescono continuamente. In Internet si trova materiale relativo a HTML, PDF, XML, formati grafici, CGI e perfino interi Web Server. performante Python è un linguaggio interpretato. In questo caso “interpretato” non è sinonimo di lento, infatti Python “compila” il proprio codice in un bytecode molto efficiente. Questo permette di raggiungere prestazioni vicine ai linguaggi in codice nativo. Inoltre Python implementa molte strutture dati e funzioni come componente intrinseca del linguaggio. Queste strutture sono dette “built-in types and tools” e sono state sviluppate con accurata efficienza. gestisce la memoria automaticamente Come in Java, in Python esiste il meccanismo di “garbage collection“, che permette di liberare il programmatore dall’ansia di allocazione selvaggia della memoria. integrabile con altri linguaggi Python può essere integrato ad altri linguaggi come .NET con IronPython o Python per .NET, Java con Jython 5 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, osserviamone alcuni più da vicino: Programmazione GUI Ecco alcune delle possibili librerie da utilizzare per sviluppare interfacce utente: - TkInter, la libreria GUI open source più usata. Gli script Python che usano TkInter sono portabili quindi avviabili su piattoforma Windows, Linux, Unix, Mac - PyQt, è la versione Python di Qt, il framework multipiattaforma storicamente realizzato da Nokia, quindi avviabile su piattaforme sia desktop (Win, Mac, Linux), sia mobile (Symbian) - wxPython, è un interfaccia Python per la libreria wxWidgets, anch’essa portabile e quindi avviabile sulle diverse piattaforme Sviluppo Web Esistono svariate possibilità per lo sviluppo Web sia ad alto che a basso livello. Ad alto livello esistono i web framework come: - Django è un ottimo framework Web, ha un sito ben curato e si trova molta documentazione, - Web2py altro ottimo framework facile da usare. 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. Mentre se si vuole scendere più a basso livello esistono le interfacce socket ma anche alcuni framework che aiutano la 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 (mediante delle interfacce ODBC con Oracle, mysql, PostgreSQL e altri), per la realizzazione di giochi (pygame e pykyra sono due ottimi framework che permettono lo sviluppo in modo semplice e intuitivo), per realizzare applicazioni scientifiche (SciPy è un tool open source per matematica, scienze e ingegneria), per grafica 3D e in tanti altri campi. Chi usa Python Python risulta un ottima soluzione per amministratori di sistema abituati ad usare linguaggi di scripting come Perl, Bash, Tlc, etc, è altrettanto un ottima soluzione per lo sviluppatori software che usano linguaggi come (C, C++, Java, etc). Oggi Python viene utilizzato in molte grandi realtà del mercato informatico, eccone alcuni esempi: 6 La NASA usa Python per lo sviluppo di sistemi di controllo; Yahoo! ha sviluppato in Python alcuni servizi di internet; Google, Youtube, RedHat usano Python. Installare Python La versione di Python che andremo ad usare in questa guida è la 3.2.3 che si può liberamente scaricare dal sito ufficiale: http://www.Python.org/ Installare Python su Windows Nella pagina di download è possibile trovare il file .msi per le versioni Windows a 32bit e 64bit. È sufficiente lanciarlo. Dopo l’installazione, Python di default viene collocato in C:\Python32. A questo punto è già possibile utilizzarlo da: Start->Tutti i programmi->Python3.2->Python. Se si vuole però avviare Python senza problemi da una finestra DOS, bisogna andare in: Pannello di controllo->Sistema->Impostazioni di Sistema Avanzate->Variabili d'ambiente Quindi modificare la variabile Path dalle variabili di sistema aggiungendo C:\Python32. Adesso se si vuole verificare che tutto è andato a buon fine, avviare il prompt dei comandi (Start>Esegui->cmd) e digitare: echo %PATH% quindi se si ritrova la variabile C:\Python32 è possibile avviare Python digitando semplicemente Python. 7 Installare Python su Linux Nei sistemi Linux spesso Python è già presente nel setup di base, è comunque possibile verificare che ci sia digitando da shell: $ Python Se il comando sarà riconosciuto, avremo lanciato l’interprete iterattivo di Python, che si presenterà con il consueto prompt composto di 3 caratteri “maggiore” ( >>> ). Se invece il comando non sarà riconosciuto, sarà necessario installarlo usando il gestore dei pacchetti per sistemi Linux, apt per distribuzioni basate su Debian, rpm per distribuzioni basate su Red Hat, oppure compilare i sorgenti. Vediamo più nel dettaglio l’installazione da Package, in base al sistema in uso è possibile avviare uno dei seguenti comandi: Comando $ yum install Python3 $ apt-get install Python3 Descrizione (per distribuzioni basate su pacchetti rpm, come Red Hat, Fedora, Centos) (per distribuzioni basate su Debian, come Ubuntu) Infine vediamo come installare Python compilando i sorgenti. Per prima cosa verificare l’ultima versione dei sorgenti dal sito , quindi lanciare i seguenti comandi: $ cd /tmp $ wget http://www.Python.org/ftp/Python/3.x/Python-3.x.tar.bz2 ed estrarre il contenuto: 8 $ tar -xjf Python-3.x.tar.bz2 $ cd Python-3.x $ ./configure $ make $ sudo make install Installare Python su Mac Come nei sistemi Linux, anche nei Mac dovremmo trovare Python già pronto all’uso. Per verificare al solito basta avviare una finestra di terminale e digitare Python, è probabile che appaia la versione 2.7, ma noi vogliamo usare la 3.2.3. Quindi bisognerà andare sul sito e scaricare la versione per MAC OS più adatta alle nostre esigenze, infine procedere all’installazione. Al termine dell’installazione, lanciando Python da terminale apparirà ancora la vecchia versione, pertanto bisogna aprire il terminale e digitare: vim ~/.bash_profile Una volta aperto il file, aggiungere la seguente riga: alias Python="Python3" Ora provando di nuovo a lanciare Python da terminale, per default si avvierà la versione 3.2.3. 9 L’interprete Python e l’IDLE Come abbiamo visto nelle lezioni precedenti, se lanciamo il comando Python da riga di comando sia in ambiente Windows che Linux, si avvia quello che da adesso chiameremo “interprete interattivo“, dall’aspetto del tutto simile ad un’altra riga di comando. Appare uno specifico prompt, caratterizzato da 3 caratteri di maggiore (>>>). In altre parole un interprete Python è un programma che permette l’esecuzione di altri programmi. È interessante aprire una piccola parentesi su quello che succede quando si esegue un programma scendendo un po’ più nel dettaglio. Ogni volta che viene invocato il comando Python il codice scritto viene scansionato per token, questi token vengono analizzati dentro una struttura ad albero che rappresenta la struttura logica del programma, che in fine viene trasformata in bytecode (file .pyc o .pyo). Per potere eseguire questi bytecode serve un interprete bytecode che è una macchina virtuale Python(PVM). Sorgente (file.py) -> Bytecode(file.pyc) -> Runtime(PVM) Esecuzione a riga di comando Torniamo a concentrarci sul funzionamento base dell’interprete Python. La caratteristica fondamentale è che digitando dei comandi si ottiene subito una risposta, ecco degli esempi: >>> 5*3 15 >>> a=5 >>> b=6 >>> 2*(a+b)+3*a 37 10 Mandare in esecuzione file .py Ma come si fa a creare ad eseguire un file Python? Per prima cosa bisogna creare un file di testo (ASCII) e salvarlo con estensione “.py“, per esempio possiamo chiamare un file helloworld.py. A questo punto possiamo aprire il file creato con un qualsiasi editor di testi (non Word, ma notepad o simili) e scrivere: print('Hello World!!') Il file contiene una sola riga di codice con la funzione print(), che come risultato stamperà una stringa. Eseguiamo il file: C:\Python32>Python helloworld.py Hello World!! Usare l’IDLE Ora che abbiamo visto brevemente come eseguire un file Python da command line, possiamo esaminare un ambiente visuale che permette di modificare, eseguire e fare il debug di un programma Python da un’unica interfaccia: l’Integrated Development Environment (IDLE). L’IDLE è un ambiente GUI Python eseguibile in ambiente Windows, Linux, MAC OS. Esso viene avviato da uno script Python (idle.pyw) nel caso di Windows 7 ad esempio si trova in: C:\Python32\Lib\idlelib\ È possibile però lanciarlo in modo veloce dal menu Start->Tutti i Programmi->Python 3.2. Avviando l’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. 11 Iniziamo a prendere confidenza con questo strumento. Aprire e lanciare un file Come appare naturale, per aprire un file Python possiamo agire da menu File->Open (Ctrl+O), per crearne uno nuovo utilizziamo File->New Window (Ctrl+N). In entrambe i casi sarà presente tra i comandi menu Run. Selezionado Run->RunModule (F5) sarà possibile eseguire lo script. Personalizzare l’IDE Sarà importante anche personalizzare l’aspetto estetico dell’IDE, per lavorare con il maggior comfort possibile. Per questo andiamo da menu su Options->Configure IDLE 12 Possiamo scegliere il tipo Font, la larghezza dell’indentazione (più avanti vedremo di cosa si tratta) dal tab Fonts/Tabs, i colori delle parole chiave, delle stringhe, dei commenti, delle definizioni, degli errori e altri dal tab Highlighting. 13 Le funzioni print e input In questa lezione facciamo la conoscenza di due funzioni basilari di Python: print e input. Utilissime soprattutto a supporto della creazione dei primi test e programmi a riga di comando. Print Abbiamo già trovato nelle passate lezioni la funzione print(), che serve a “stampare” in output (tipicamente sullo schermo) il valore di una variabile o di una espressione. >>> a = 12 >>> b = 3 >>> print(a,b,(a - b)) 12 3 9 Si noti la flessibilità del comando print, il quale concatena il valore di diversi parametri suddivisi dalla virgola e li mostra in output. Nota: nelle precedenti versioni di Python print era un comando e non una funzione e non era necessario inserire i parametri tra parentesi, mentre ora omettere le parentesi significa commettere un errore di sintassi. Quando si lavora a riga di comando è sufficiente digitare il nome della variabile per stamparne il valore. Questa possibilità offerta dalla modalità interattiva, consente un debug un po’ più snello. Quando si esegue un programma è necessario utilizzare il print. >>> x=10 >>> x 10 Input Se realizziamo piccoli programmi da utilizzare da console o riga di comando, risulta utile la funzione input, che serve ad intercettare le sequenze inserite nello std-in, quindi tipicamente da tastiera. Ecco due esempi: >>> valore = eval(input('Inserisci un valore numerico:')) Inserisci un valore numerico: 5 >>> print(valore*valore) 25 14 >>> valore = input('Inserisci una stringa:') Inserisci una stringa: html.it >>> print(valore) html.it Nota: In passato il comando input serviva per l’acquisizione di variabili numeriche, mentre per le sequenze e le stringhe si utilizzava raw_input. Nella versione 3 di Python il comando raw_input diventa input. Per ottenere il precedente comportamento di input occorre scrivere eval(input(...)) 15 Indentazione e blocchi di codice in Python In questa lezione vediamo alcuni degli aspetti fondamentali da tener presente nella programmazione con Python, il più importante certamente è la caratterizzazione dei blocchi di codice attraverso l’indentazione del testo, una pratica che può spiazzare chi viene da altri linguaggi di programmazione in cui si delimitano i blocchi con parentesi graffe, begin-end o altri costrutti. Vedremo anche come dichiarare le variabili e commentare il codice. Indentazione e blocchi di codice Python è stato progettato e realizzato per essere un linguaggio chiaro e leggibile, per questo è stato scelto di utilizzare l’indentazione per definire i blocchi di codice o il contenuto dei cicli di controllo. All’inizio ci si può confondere con l’indentazione e i blocchi, e può anche capitare che gli spazi, l’indentazione cambiano in funzione dell’editor che si usa, ma il vantaggio è quello di avere un alta leggibilità del codice, proprio perché si è obbligati ad indentare i sorgenti. Vediamo in generale come viene rappresentata l’indentazione di blocchi nel caso dei cicli di controllo: istruzione: blocco istruzione: blocco È molto importante fare attenzione all’indentazione quindi, perché viene considerata nel processo di parsing del programma, ecco cosa succede se introduciamo qualche inesattezza: 16 Spazi o tabulazioni? Le indentazioni devono essere fatte utilizzando gli Spazi. Così è indicato anche nelle guideline ufficiali e la motivazione principale sta nel fatto che non c’è uniformità di rappresentazione delle tabulazioni tra diversi editor, il ché potrebbe indurre in errore anche il più attento dei programmatori. Quanti spazi? In genere si utilizzano indentazioni di quattro spazi, ma possiamo decidere arbitrariamente, ciò che conta è che siano coerenti i livelli di indentazione, ovvero le distanza tra le istruzioni interne al blocco di codice e quelle esterne. Se pensate di mettere a disposizione il vostro codice ad altri sviluppatori, al di fuori di un team, è consigliabile adeguarsi ai 4 spazi canonici, mentre se si fa parte di un team di sviluppo sarebbe utile, ma è anche ovvio, che tutti condividano lo stesso stile per le indentazioni. Altra cosa sono gli spazi utilizzati all’interno delle istruzioni, per i quali invece abbiamo piena libertà, nel rispetto della sintassi. Ad esempio, all’interno della definizione di un array possiamo utilizzare anche una indentazione di questo tipo: >>> vec = [ 'uno', 'dieci', 'cinque' ] >>> print(vec) ['uno', 'dieci', 'cinque'] 17 Variabili e commenti in Python In Python non è necessario definire le variabili prima di utilizzarle e nemmeno assegnare ad esse un tipo. Il tutto avviene implicitamente mediante l’istruzione di assegnamento (=), un po’ come in JavaScript o nel vecchio Basic. Python possiede tutti i classici tipi di dati, comuni agli altri linguaggi. Ecco una tabella che riassume le caratteristiche principali dei tipi di dati disponibili: tipo di dato rappresentazione interna esempi oltre 32 bit con crescita in base alle esigenze 999999999, 1200, -56, 0 Intero Reale 32 bit (tipo double del C) 1.23 3.14e-10, 4.0E210 Booleano intero con 1=VERO e 0=FALSO (come in C) 0, 1 Complesso coppia di numeri reali 3+4j, 5.0+4.1j, 3j Stringhe lista di caratteri 'stefano', "l'acqua" Ricordiamo che le regole da seguire nella scelta dei nomi delle variabili è simile a quella dei più comuni linguaggi di programmazione, in particolare: Ogni variabile deve iniziare con una lettera oppure con il carattere underscore “_”, dopodiche’ possono seguire lettere e numeri o il solito underscore. Python è un linguaggio case sensitive, quindi distingue le variabili composte da caratteri minuscoli da quelle scritte con caratteri maiuscoli. Esistono delle parole riservate che non possono essere utilizzate per i nomi delle variabili. Esse sono e seguenti: False, None, True, and, as, assert, break, class, continue, def, del, elif, else, except, exec, finally, for, from, global, if, import, in, is, lambda, nonlocal, not, or, pass, print, raise, return, try, while, with, yield In Python è possibile assegnare un valore ad una variabile mediante l’operatore “=“. Assegnamento multiplo Una singolare possibilità offerta da Python è rappresentata dall’assegnamento multiplo nel quale si possono inizializzare più variabili direttamente sulla stessa riga di codice. >>> a = 'viva Python' >>> b = 3 >>> c, d = 'su', 'HTML.it' # assegnamento multiplo >>> print (a,b,c,d) viva Python 3 su HTML.it cognome, nome = input("Inserisci cognome e nome: ").split() >>> print (nome,cognome) #supponendo di ave inserito: Simpson Homer Homer Simpson 18 I commenti in Python È sempre buona norma commentare il codice, e quindi anche Python offre una sintassi per i commenti. Il carattere che identifica il commento è : “#“. esso si può usare all’inizio della riga oppure alla fine di una istruzione. # Questo è un commento all'inizio della riga # si possono fare quante righe si preferisce a = 3 # Questo è un commento che segue l'istruzione di assegnamento 19 Numeri e operatori logici in Python Python offre quattro possibilità per rappresentare i numeri: interi, razionali, complessi e booleani. Nota: Nelle versioni precedenti di Python esisteva una distinzione tra intero (int)e “intero lungo” (long), da Python 3 si parla solo di int che corrisponde al vecchio long (in pratica long è stato rinominato int). Oltre a questi tipi di dati “semplici”, Python offre la possibilità di dichiarare anche strutture dati complesse implementate in modo nativo (“built-in types” come si dice ufficialmente): liste, dizionari, tuple e files; che esamineremo nelle prossime lezioni. L’aritmetica utilizzata è molto simile a quella del C, facciamo qualche esempio: >>> 6+4*3 18 >>> 9/2 # la divisione tra due numeri interi restituisce un float 4.5 >>> 2j * 2j # moltiplicazione tra numeri complessi (-4+0j) >>> int(10.6) # conversione esplicita dal tipo float al tipo int 10 >>> float(20) # conversione esplicita dal tipo int al tipo float 20.0 Le operazioni sui tipi numerici sono le più classiche: Operatore + - somma e sottrazione Esempi 10+12=22 5-1=4 moltiplicazione e divisione 10*12=120 10/2=5 % resto della divisione 10%3=1 5.3%2.5=0.3 // divisione intera 10//3=3 shift bit a bit a sinistra e destra 24<<1=48 10>>1=5 * / << >> 20 Descrizione Operatori logici in Python Per gli operatori logici (booleani) abbiamo: Operatore Descrizione or e and logici or and < <= > not >= == ¦ & ^ negazione logica <> != Esempi x or y z and k (not 0)=1 or bit a bit (10==10)=1 ('a’!='a’=0 x ¦ y and bit a bit x & y or esclusivo bit a bit x ^ y operatori di confronto 21 Stringhe in Python In questa lezione ci occupiamo di come trattare le stringhe in Python. Abbiamo già visto che per dichiarare una stringa sia sufficiente assegnare ad una nuova variabile un testo racchiuso tra virgolette: è possibile racchiudere il suo valore indifferentemente tra apici ' o doppi apici “ Questo permette di superare facilmente il problema dell’utilizzo dei suddetti caratteri nel valore stesso della stringa. Ricordo comunque che è possibile forzare l’inserimento di ognuno dei due apici semplicemente raddoppiandoli. Per iniziare ad approfondire un po’, iniziamo col dire che le stringhe possono essere considerate come sequenze di caratteri (liste), è pertanto possibile prelevare i caratteri attraverso il loro indice, tenendo presente che si parte sempre da 0. Slicing e sotto-stringhe Ancora più interessante è la possibilità di prelevare sottostringhe utilizzando la tecnica dello “slicing” (affettare). Questa tecnica permette di tagliare una parte della lista indicando l’indice di partenza e l’indice finale. È utile sapere però che la sotto-stringa prelevata è quella che va dal carattere indicato del primo indice incluso, fino quello dell’indice finale escluso. Omettendo uno dei due indici indichiamo a Python di andare fino in fondo alla stringa. Infine, se inseriamo un numero negativo come indice finale, Python conterà i caratteri a ritroso. Operatore + * s[i] Descrizione concatenamento ('a'+'b')='ab' ripetizione ('a'*3)='aaa' indicizzazione dei caratteri s='abc' s[0]= 'a' s='abc' s[1:2]='b' s='abc' len(s)=3 s[i:j] slicing len(s) lunghezza % formattazione di stringhe Esempio (‘ciao %s’ % ‘stefano’)=’ciao stefano’ Ecco qualche esempio pratico: >>> y='ciao mondo' >>> y[0] # prende la prima lettera di y tenendo presente che si parte da 0 'c' >>> y[5] 'm' 22 >>> x='programmazione Python' >>> x[-2] # stampa il secondo carattere partendo da destra 'o' >>> x[2:7] # stampa i caratteri di x compresi da 2 a 7 (escluso) 'ogram' >>> x[4:] # stampa dal quarto carattere alla fine di x 'rammazione Python' >>> x[:4] # stampa i caratteri di x compresi dall'inizio al quarto (escluso) 'prog' >>> x[3:-2] # stampa dal terzo carattere (contando da 0) al penultimo di x (escluso) 'grammazione pyth' Metodi per le stringhe Osserviamo ora alcuni metodi per le stringhe che possono tornare utili. Il metodo find() serve a ricercare dei caratteri da una stringa (restituisce -1 se non è presente) >>> s='html.it' >>> s.find('html') 0 Il metodo replace() serve a sostituire dei caratteri in una stringa. >>> s.replace('it','test') 'html.test' I metodi upper() e lower() servono invece a convertire in maiuscolo e minuscolo una stringa. >>> up=s.upper() >>> print(up) HTML.IT >>> low=up.lower() >>> print(low) html.it 23 Costruire le stringhe Grazie all’operatore percentuale (%) possiamo costruire una stringa inserendo dei parametri “segnaposto” da sostituire con i valori assunti da variabili, come avviene nella istruzione printf del C. Facciamo subito qualche esempio complesso per osservarne il funzionamento: >>> nome = 'stefano' >>> eta = 29 >>> risultato = "%s ha %d anni" % (nome, eta) >>> print(risultato) stefano ha 29 anni Nelle prime righe abbiamo definito le variabili e assegnato valori stringa e numerici. Nella stringa “%s ha %d anni” abbiamo inserito i parametri: %s : parametro di tipo stringa %d : parametro di tipo numerico Parametri per la costruzione delle stringhe Parametro %s %c %d %u %o %x %g %e Descrizione stringa singolo carattere numero decimale intero senza segno numero in notazione ottale numero in notazione esadecimale numero reale in notazione normale numero reale in notazione scientifica Python offre altre funzioni per gestire le stringhe, esse sono tutte raggruppate nel modulo denominato “string”. Non avendo ancora parlato di moduli, introdurremo queste funzioni in seguito. 24 Liste Le liste in Python sono collezioni ordinate di oggetti, simili agli array di altri linguaggi di programmazione. In altre parole permettono di memorizzare una sequenza gli oggetti ed accedere ad essi mediante un indice. A differenza delle stringhe che abbiamo trattato, le liste possono contenere oggetti eterogenei come numeri, stringhe e altre liste. Questo consente di manipolare con agilità strutture complesse con dati di diversa natura. Per dichiarare una lista è sufficiente assegnare ad una variabile un elenco di valori, separati da virgola e racchiusi da parentesi quadre. Ecco un paio di esempi: >>> l1=[1, 'html.it'] # lista con un intero e una stringa >>> l2=[1, 1.2, 'ciao', [1,2] ] # lista con: intero, reale, stringa e un'altra lista Molto di quanto detto per le stringhe vale anche per le liste, come l’indice che inizia da 0. Gli esempi seguenti illustrano gli operatori di indicizzazione e slicing che abbiamo già visto per le stringhe. >>> s=['html', 'it', 'html.it'] >>> s[0] # indicizzazione 'html' >>> s[-2] # indicizzazione contando da destra 'it' >>> s[:2] # slicing ['html', 'it'] >>> s[2:] ['html.it'] >>> s[1:-1] ['it'] Vediamo adesso gli operatori per la concatenzazione “+” e ripetizione “” per le liste: >>> [1, 2, 3] + [4, 5] # concatenazione [1, 2, 3, 4, 5] >>> ['html.it']*3 # ripetizione 25 ['html.it', 'html.it', 'html.it'] Si possono anche modificare, eliminare degli elementi da una lista tramite l’indicizzazione: >>> y=[1,2,3] >>> y[0]="uno" # modifica della lista >>> y ['uno', 2, 3] >>> y[1:2]=[] # cancello un elemento dalla lista >>> y ['uno', 3] Per conoscere la lunghezza di una lista utilizziamo la funzione len() : >>> lista=['c', 'a', 'b'] >>> len(lista) 3 Metodi per le liste INSERIMENTO Metodo Descrizione append(oggetto) Aggiunge un oggetto in coda alla lista extend(lista) Collega un’altra lista in coda alla lista insert(indice ,oggetto) Inserisce un oggetto in testa alla lista Esempi: >>> lista=['html', 'HTML', 'Html'] >>> lista.append('html.it') #metodo append() >>> lista ['html', 'HTML', 'Html', 'html.it'] >>> lista.extend(['HYml','html.IT']) >>> lista ['html', 'HTML', 'Html', 'html.it', 'HYml', 'html.IT'] 26 #metodo extend() >>> lista.insert(0, 'primoHTML') #metodo insert() >>> lista ['primoHTML', 'html', 'HTML', 'Html', 'html.it', 'HYml', 'html.IT'] CANCELLAZIONE Metodo pop([indice]) remove(valore) Descrizione Restituisce l’oggetto della lista con l’indice specificato, poi lo rimuove dalla lista. Se l’indice non è specificato, pop elimina e restituisce l’ultimo elemento della lista. Elimina dalla lista il valore passato Esempi: >>> lista=['html', 'HTML', 'Html'] >>> lista.pop() #senza indice elimina l'ultimo elemento della lista 'Html' >>> lista ['html', 'HTML'] >>> lista=['html', 'HTML', 'Html'] >>> lista.remove('html') ['HTML', 'Html'] >>> lista=['html', 'HTML', 'Html'] >>> lista.remove(lista[1]) >>> lista ['html', 'Html'] 27 ORDINAMENTO Metodo sort() Descrizione Restituisce la lista ordinata in ordine crescente >>> lista=['c', 'a', 'b'] >>> lista.sort() >>> lista ['a', 'b', 'c'] 28 Esempi: Dizionari Un dizionario rappresenta una collezione “non ordinata” di oggetti. Gli oggetti sono identificati univocamente da una chiave (generalmente una stringa) invece che mediante un indice numerico, come avviene nelle liste. Ogni elemento del dizionario è rappresentato da una coppia (chiave : valore), la chiave serve per accedere all’elemento e recuperare il valore. Esattamente ciò che avviene quando si cerca una parola sul vocabolario, in tal caso il valore che si cerca è una frase che spiega il significato della chiave. Per fare un parallelo con altri linguaggi si può pensare al dizionario come ad una specie di array associativo. Dal punto di vista computazionale è vantaggioso utilizzare i dizionari perché sono delle tabelle hash. Come il tipo hash in Perl, le istanze di Hashtable in Java o C#, le mappe MFC per Visual C++, etc. In analogia alle liste, anche per i dizionari Python lascia la possibilità di inserire oggetti eterogenei nella stesso dizionario. Un dizionario si rappresenta con gli elementi separati da virgole e racchiusi in parentesi graffe, un dizionario vuoto è rappresentato dalle parentesi aperte e chiuse: >>> diz1={} # dizionario vuoto >>> diz1 {} Possiamo creare un dizionario vuoto anche sfruttando il metodo dict(): >>> diz2=dict() # dizionario vuoto >>> diz2 {} Possiamo definire gli elementi del dizionario direttamente elencando le coppie chiave-valore: >>> diz={'html':1, 'HTML':2, 'HTml':3} >>> diz {'html': 1, 'HTML': 2, 'HTml': 3} Per recuperare un valore possiamo poi passare al dizionario il nome della chiave relativa. >>> diz['HTML'] # recupera il valore partendo dalla chiave 2 29 Possiamo verificare l’esistenza di una certa chiave nel dizionario con la keyword in: >>> 'html' in diz # ricerca la chiave >>> True Inserimento, modifica e cancellazione Vediamo adesso come si fa a inserire, modificare e cancellare gli elementi di un dizionario. >>> diz={'html':1, 'HTML':2, 'HTml':3} >>> diz['html.it']=4 # inserimento >>> diz {'html': 1, 'HTML': 2, 'html.it': 4, 'HTml': 3} >>> diz['html']=7 # modifica >>> diz {'html': 7, 'HTML': 2, 'html.it': 4, 'HTml': 3} >>> del diz['HTML'] # cancellazione >>> diz {'html': 7, 'html.it': 4, 'HTml': 3} Per conoscere il numero di elementi di un dizionario possiamo utilizzare la consueta funzione len. >>> len(diz) 3 Metodi per i dizionari Vediamo ora quali sono i metodi principali dei dizionari: Campo Descrizione get(‘chiave’) restituisce il valore della chiave inserita 30 values() restituisce l’elenco dei valori del dizionario keys() restituisce l’elenco delle chiavi del dizionario items() restituisce una lista di tuple per ogni coppia >>> diz={'html.it':1, 'HTML':2, 'html':3} >>> diz.get('html') # metodo get() 3 >>> diz.values() # metodo values() dict_values([1, 3, 2]) >>> diz.keys() # metodo keys() dict_keys(['html.it', 'html', 'HTML']) >>> diz.items() # metodo items() dict_items([('html.it', 1), ('html', 3), ('HTML', 2)]) Ordinamento delle chiavi A differenza di quanto accadeva prima di Python 3.x, non possiamo ordinare un dizionario utilizzando direttamente l’elenco ottenuto dal metodo keys. Facciamo un esempio supponiamo di voler elencare in ordine alfabetico un dizionario che rappresenta gli studenti con i rispettivi voti in una materia. Ecco cosa accadrebbe se tentiamo il vecchio approccio: >>> diz1 = {'andrea':28, 'giuseppe':25, 'mario':21, 'elena':28} >>> k = diz1.keys() >>> k.sort() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'dict_keys' object has no attribute 'sort' Come workaround possiamo ottenere una vista ordinata convertendo in una lista l’elenco delle chiavi e applicare a quel punto il metodo sort. >>> k=list(k) #conversione in lista >>> k ['mario', 'giuseppe', 'andrea', 'elena'] 31 >>> k.sort() >>> k ['andrea', 'elena', 'giuseppe', 'mario'] >>> for elemento in k: print(elemento, diz1[elemento]) ... andrea 28 elena 28 giuseppe 25 mario 21 32 Tuple Una tupla in Python è molto simile ad una lista: il suo funzionamento è per lo più il medesimo, cerchiamo quindi di capire il funzionamento della tupla esaminando le differenze con la lista. La prima differenza tra tupla e lista è sintattica: le tuple sono racchiuse tra parentesi tonde (e non quadre come le liste). Per rappresentare una tupla vuota si usa la seguente sintassi: >>> t=() >>> t () Mentre per rappresentare una tupla con un elemento si usa la sintassi: >>> t=(1,) >>> t (1,) La seconda differenza è forse più sottile ma è sostanziale: La lista è un tipo mutabile La tupla è un tipo non mutabile Tipi mutabili Cerchiamo di chiarire questo concetto di Python: le variabili di tipo mutabile possono cambiare di stato durante la loro vita, infatti per una lista è possibile aggiungere o togliere elementi in qualsiasi momento. Per i tipi non mutabili ciò non è possibile, è possibile solamente cambiare in blocco l’intero valore. Usare le tuple Le tuple sono utilizzate quando si deve essere certi che nessuno possa modificare il contenuto dell’elenco, e quindi non si possa aggiungere o togliere elementi. Gli operatori sono gli stessi delle liste (a parte quelli che mutano il valore, che chiaramente non hanno motivo di esistere), vediamo alcuni esempi di indicizzazione e slicing: >>> tupla=(1,2,3,4) >>> tupla (1, 2, 3, 4) 33 >>> tupla[3] #indicizzazione 4 >>> tupla[1:-1] #slicing (2, 3) Come per le stringhe e per le liste abbiamo gli operatori di concatenazione e ripetizione: >>> ('primo','secondo')+('terzo','quarto') # concatenazione ('primo', 'secondo', 'terzo', 'quarto') >>> ('primo',)*2 # ripetizione ('primo', 'primo') Come accennato in precedenza le tuple a differenza delle liste non possono subire modifiche alla struttura o al singolo valore: >>> tupla=(1,2,3) >>> tupla.append(4) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'tuple' object has no attribute 'append' >>> tupla.remove(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'tuple' object has no attribute 'remove' >>> tupla[0]=2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment 34 A cosa servono le tuple? Le tuple sono più veloci delle liste. Se si vuole un gruppo costante di valori e l'unica cosa che si intende fare è iterare al suo interno, si definisce una tupla invece di una lista. Il codice risulta più sicuro “proteggendo dalla scrittura” i dati che non devono essere modificati. Usare una tupla invece di una lista è come avere un'implicita istruzione assert che mantenga il dato costante e che richieda una riflessione (ed una specifica funzione) per modificarlo. Le tuple sono anche usate nelle formattazione delle stringhe. Le chiavi di un dizionario possono essere interi, stringhe e anche tuple. Le tuple possono essere usate come chiavi in un dizionario, le liste no. Poiché le chiavi di un dizionario devono essere immutabili, se si ha una tupla di liste, questa è mutabile e quindi non è sicuro usarla come chiave di un dizionario. Solo tuple di stringhe, numeri o altre tuple sicure possono essere usate come chiavi per un dizionario. Conversione liste-tuple, tuple-liste È possibile effettuare la conversione da liste a tuple e viceversa in maniera piuttosto semplice, con le funzioni list e tuple. Vediamo come: >>> tupla=(1,2,3,4) >>> lista=list(tupla) # conversione tupla-lista >>> lista [1, 2, 3, 4] >>> tuplaconv=tuple(lista) # conversione lista-tupla >>> tuplaconv (1, 2, 3, 4) 35 I file in Python Il concetto di file è piuttosto noto. Il fatto che la gestione dei file in Python sia implementata internamente al linguaggio (built-in), garantisce una notevole velocità e semplicità nella loro gestione. In particolare è importante poter gestire i file con istruzioni del linguaggio di programmazione, per salvare delle informazioni sul disco e renderle di conseguenza persistenti. I file, come tutto in Python, sono oggetti e ad oggetti di tipo file fanno riferimento le operazioni più comuni, come aprire e chiudere un file, leggere e scrivere in sequenza byte sul medesimo file, etc. Tutto ciò vale per file di testo e file binari. Ecco una tabella con le principali funzioni built-in per la gestione dei file: Operazione Descrizione output = open('pippo.txt','w') apertura di un file in scrittura input = open('dati','r') apertura di un file in lettura s = input.read() lettura dell’intero contenuto del file s = input.read(N) lettura di N bytes s = input.readline() lettura di una riga (per files di testo) s = input.readlines() restuisce l’intero file come lista di righe (per files di testo) output.write(s) scrive nel file i valori passati come parametri e ritorna il numero di bytes scritti output.writelines(L) scrive la lista L in righe nel file output.close(L) chiusura del file Le variabili input e output della tabella sono oggetti di tipo file. Ecco un altro semplice esempio: >>> miofile = open('html.txt','w') >>> miofile.write('riga 1\n') # apre il file in scrittura # scrive riga 1 # scrive riga 2 # scrive riga 3 7 >>> miofile.write('riga 2\n') 7 >>> miofile.write('riga 3\n') 36 7 >>> miofile.close() >>> miofile = open('html.txt','r') >>> miofile.readlines() lista di righe # chiude il file # apre il file in lettura # legge dal file restituisce una ['riga1\n', 'riga2\n', 'riga3\n'] La funzione open apre il file in scrittura e ritorna un oggetto di tipo file che associamo a miofile. Utilizziamo l’istanza creata per scrivere nel file con il metodo write e lo chiudiamo con close. Poi riapriamo lo stesso file in lettura e ne stampiamo il contenuto con readlines(). Modalità di apertura di open Olre a "w" e "r" (scrittura e lettura), che abbiamo già visto, la funzione open consente altre modalità per l’apertura dei file, che possiamo passarle come secondo parametro: Modalità "r" "w" "rb" e "wb" "w+" e "w+b" "r+" e "r+b" Descrizione Apre un file di testo in lettura, equivalente a "rt", se si omette il secondo parametro di open è la modalità di default Apre un file in scrittura, e ne azzera i contenuti Apre un file binario in lettura o scrittura con azzeramento del contenuto Aprono un file in modifica, rispettivamente testuale e binario, e consentendone l’aggiornamento, ma ne azzerano i contenuti Aprono un file in modifica, rispettivamente testuale e binario, e ne consentono l’aggiornamento senza azzerare i contenuti Dai file aperti in modalità binaria possiamo estrarre i dati come oggetti byte senza alcuna codifica. Per sapere tutto sulle modalità di apertura dei file possiamo interrogare Python: >>> print(open.__doc__) Muoversi all’interno del file Come per C o altri linguaggi evoluti anche con Python possiamo spostarci all’interno dei file, ecco le principali metodi dell’oggetto file: Metodo Descrizione Ritorna la posizione attuale del cursore all’interno del file seek(pos, rel) Sposta la posizione del cursore all’interno del file, con il primo parametro indichiamo il numero di byte (positivo o negativo) di cui vogliamo spostarci, il secondo parametro indica il punto di partenza ( 0=dell’inizio del file; 1=dalla posizione attuale; 2:dalla fine del file) tell() 37 read(N) Legge N bytes dalla posizione corrente e sposta il cursore avanti di N posizioni Infine per verificare lo stato del file è sufficiente stampare i dati dell’istanza corrente. Esempio: >>> miofile <_io.TextIOWrapper name='html.txt' mode='r' encoding='cp1252'> Anche se sembra scontato è utile ricordare di chiudere sempre i file ( close()), dopo averli utilizzati. 38 Cicli e istruzioni condizionali In questa lezione vediamo come sono concepiti i classici costrutti condizionali e le iterazioni in Python e come si utilizzano in pratica. if-elif-else Il costrutto if-elif-else permette di eseguire istruzioni o gruppi di istruzioni diverse a seconda del verificarsi di una condizione: if condizione1: <gruppo di istruzioni 1> elif condizione2: # opzionale <gruppo di istruzioni 2> ... elif condizione(n-1): # opzionale <gruppo di istruzioni (n-1)> else: # opzionale <gruppo di istruzioni n> Le condizioni sono seguite dai due punti ( : ) e il codice che segue deve essere indentato secondo la convenzione di Python. Possiamo inserire quante condizioni alternative ( elif ) desideriamo e alla fine una condizione che è alternativa a tutte le altre ( else ), questo comportamento è simile anche quello dello switch-case-break che troviamo in altri linguaggi come il C. Trattandosi di condizioni tutte alternative, solo la prima che viene verificata causa l’esecuzione del codice al suo interno: tutte le successive saranno ignorate anche se verificate. Per fare un esempio, supponiamo di voler scrivere una semplice lista della spesa e controllare la quantità di ogni oggetto da comprare, per fare questo utilizziamo un dizionario con valori statici per semplicità: spesa={'biscotti':3,'pane':1,'pasta':3,'legumi':1} >>> spesa {'biscotti': 2, 'legumi': 1, 'pane': 1, 'pasta': 3} Adesso cerchiamo il primo dei valori maggiore di 2 in un certo ordine (pasta, biscotti, pane, legumi). 39 >>> if spesa.get('pasta')>2: print('Attenzione valore pasta:', spesa.get('pasta') elif spesa.get('biscotti')>2: print('Attenzione valore biscotti:', spesa.get('biscotti')) elif spesa.get('pane')>2: print('Attenzione valore pane:', spesa.get('pane')) elif spesa.get('legumi')>2: print('Attenzione valore legumi:', spesa.get('legumi')) else: print('Tutto ok!') Attenzione valore pasta: 3 Anche la condizione sui biscotti era verificata ma viene ignorata. Ciclo while Il ciclo while è il più generico tra i cicli perché non strettamente collegato a variabili, ma ad una semplice condizione di controllo. Eccone la sintassi in Python: while condizione: <gruppo di istruzioni> Il comportamento prevede l’esecuzione delle istruzioni all’interno de ciclo finché la condizione rimane verificata. Si esce dal ciclo (o non vi si entra proprio) quando la condizione è falsa. Ecco un esempio: >>> a=0 >>> b=10 >>> while a<b: print(a) a=a+1 0 1 2 3 4 5 6 7 8 9 40 Ciclo for Il ciclo for è un’altro dei costrutti più noti e comuni nei linguaggi di programmazione, tipicamente prevede un contatore che viene incrementato fino al raggiungimento di un certo valore, che stabilisce l’uscita dal ciclo. Vediamo come è concepito in Python, ecco la sintassi: for <contatore del ciclo> in <lista>: <gruppo di istruzioni> Il contatore del ciclo è una variabile alla quale viene assegnato, ad ogni passo del ciclo, un valore della lista. In questo modo all’interno del gruppo di istruzioni è possibile operare sul singolo elemento della lista. Vediamo un esempio: >>> lista = [1,2,3,4] >>> newLista =[] >>> for i in lista: if i>2: newLista.append(i) print(newLista) [] [] [3] [3, 4] Il costrutto for sembra molto limitato perché, a differenza di altri linguaggi di programmazione, permette di operare solamente sulle liste. In realtà questa piccola limitazione viene facilmente superata utilizzando la funzione range. range La funzione range permette di costruire una lista di numeri partendo da 0 fino ad un valore scelto, in questo modo è sufficiente creare la lista da utilizzare nel ciclo for con questa istruzione per ottenere il medesimo risultato di altri linguaggi di programmazione. Vediamo un esempio che spiega bene questo trucco: >>> lista = range(4) 41 >>> for i in lista: print(i) 0123 Oppure più brevemente: >>> for i in range(10): print(i) 0123456789 Se non si vuole cominciare da 0 basta indicare alla funzione range anche il valore iniziale: >>> for i in range(3,8): print(i) 3 4 5 6 7 Forzare l’uscita dal ciclo È sempre possibile forzare l’uscita dal ciclo (qualsiasi ciclo) in ogni momento, grazie ad alcuni comandi: Comando Descrizione break permette di saltare fuori da un ciclo ignorando le restanti istruzioni da eseguire continue permette di saltare alla prima istruzione della prossima iterazione del ciclo else sia il ciclo for che il ciclo while hanno un costrutto aggiuntivo opzionale che permette di eseguire un blocco di istruzioni all’uscita dal ciclo, queste istruzioni vengono ignorate se usciamo dal ciclo con un break Come conseguenza le sintassi dei due cicli si estendono nel seguente modo: for <contatore-del-ciclo> in <lista>: <gruppo di istruzioni 1> else: <gruppo di istruzioni 2> # non eseguita in caso di break 42 >>> a = 0 >>> b = 10 >>> while a<b a=a+1 print(a) if a==5: break else: print('sono uscito regolarmente') 1234 while <test>: <gruppo di istruzioni 1> else: <gruppo di istruzioni 2> # non eseguita in caso di break Se impostiamo b < 5 vediamo apparire la scritta “sono uscito regolarmente”, altrimenti il break impedisce l’esecuzione del blocco else del while. L’istruzione pass L’istruzione pass è un operazione nulla, quando viene eseguita non succede nulla, è utile come segnaposto. Vediamo un semplice esempio for lettera in 'HTML.it': if lettera == '.': pass # non fa nulla print('Punto catturato') continue print('Lettera attuale: ',lettera) Lettera attuale: Lettera attuale: Lettera attuale: Lettera attuale: Punto catturato Lettera attuale: Lettera attuale: H T M L i t 43 Comprehension per liste e dizionari Una comprehension è una modalità di estrazione di sottoinsiemi da liste o dizionari. È molto potente perché ci permette di agire sugli elementi dell’insieme (lista o dizionario) e filtrarli allo stesso tempo, come se definissimo in matematica il codominio di una funzione: insieme = ( f(x) | x appartiene a vecchio insieme, ad una certa condizione ) La sintassi Python per la list-comprehension e dictionary-comprehension risulta molto potente ed espressiva: lista = [espressione for variabile in altra_lista if espressione] diz = {espr_chiave : espr_valore for espr_chiave, espr_valore in altro_diz if espressione} diz = {espr_chiave : espr_valore for variabile in altra_lista if espressione} Questi strumenti permettono di compiere operazioni iterative su sequenze in maniera più immediata, flessibile ed efficiente rispetto alle consuete istruzioni cicliche che abbiamo già visto. Esempi di comprehension Il modo migliore di comprendere questi strumenti è quello di fare degli esempi. Iniziamo dalla comprehension per liste e vedremo subito l’eleganza e la potenza nella definizione e creazione di una lista. Esempio di conversione euro-dollaro: >>> euro = [2.5, 3.7, 20.9] #lista di valori da convertire in dollaro >>> dollaro = [x*1.3 for x in euro] >>> dollaro [3.25, 4.8100000000000005, 27.169999999999998] >>> dollaroFiltrato=[x*1.3 for x in euro if x>5.0] >>> dollaroFiltrato [27.169999999999998] Vediamo adesso un paio di semplici esempi di comprehension per dizionari, simili a quelli fatti per le liste. >>> x=[1,2,3,4] #data la lista x si vuole incrementare ogni valore di 100 >>> dictComp={y:y+100 for y in x} 44 >>> dictComp {1: 101, 2: 102, 3: 103, 4: 104} >>> dictCompFiltrato={y:y+100 for y in x if y>2} >>> dictCompFiltrato {3: 103, 4: 104} 45 Funzioni Le funzioni sono tra le caratteristiche più importanti dei linguaggi di programmazione. In generale possiamo considerarle come delle scatole nere che: prendono in ingresso alcuni parametri (o nessuno); compiono una certa elaborazione dei parametri o modificano variabili o oggetti già definiti; restiuiscono un risultato dell’elaborazione (non sempre le funzioni in Python restituiscono valori). Sono quindi uno strumento utile per strutturare il codice in blocchi omogenei dal punto di vista logico al fine di migliorare la lettura e la manutenzione del sorgente. Inoltre sono un primo passo verso il riutilizzo del codice, una funzione creata con responsabilità definite e il necessario livello di genericità, può tornare utile in diversi progetti. Definire una funzione in Python La sintassi per definire una funzione è molto semplice: def nome_funzione(<lista parametri separati da virgola>) <blocco istruzioni> return <risultato> Nel caso in cui non prevediamo che la funzione restituisca dei valori in uscita, omettiamo semplicemente l’istruzione return: def nome_funzione(<lista parametri separati da virgola>) <blocco istruzioni> Quando non si specifica nessun valore di ritorno, la funzione ritornerà il valore None di default. Scope Si devono distinguere le “variabili locali” alle funzioni (quindi utilizzabili solo da esse) e le “variabili globali“, ossia appartenenti al namespace del modulo (quindi utilizzabili al di fuori della funzione). Quando si utilizza una variabile, Python cerca prima il nome di quella variabile nel namespace locale. Se la ricerca non da esito positivo, si prosegue con il namespace globale e solo successivamente si va a cercare il nome tra le funzioni builtin (cioè quelle predefinite in Python stesso). Questo meccanismo permette di utilizzare il valore delle variabili globali, ma di non poterle mai modificare, in quanto un nuovo assegnamento alla variabile provoca la creazione dello stesso nome in un namespace nuovo. 46 Passaggio dei parametri Una volta definita una funzione, possiamo richiamarla semplicemente invocando il suo nome e passandole i parametri tra parentesi. Ad esempio, quando si richiama una funzione vengono trasferiti i valori delle variabili ai parametri delle funzioni, che hanno visibilità locale alla funzione. Questo permette alla funzione di venire a conoscenza di informazioni proveniente dal blocco chiamante. Facciamo subito un semplice esempio (con e senza return) creando una funzione che prende come parametri di ingresso una lista e un numero. >>> def func(lista, num): lista.append(10) num=num+3 return lista, num >>> x=[1,2] >>> y=4 >>> func(x,y) ([1, 2, 10], 7) Esempio senza valori di ritorno: >>> def func(lista, num): lista.append(10) num=num+3 >>> x=[1,2] >>> y=4 >>> func(x,y) >>> x,y ([1, 2, 10], 4) La funzione in entrambe i casi modifica la lista esterna x, mentre non succede nulla alla variabile y, questo si vede bene confrontando i due risultati: nel primo stampiamo i valori associati alle variabili che avevamo all’interno della funzione, nel secondo stampiamo l’effetto che la funzione ha avuto nei confronti delle variabili esterne. 47 In genere nei linguaggi di programmazione esistono due modalità di passaggio dei parametri: Passaggio per valore: viene trasferita alla funzione solo una copia della variabile, quindi la funzione non può alterare il valore di quella variabile. Passaggio per riferimento: viene trasferito il riferimento alla variabile [si potrebbe pensare come l'indirizzo di una casella in memoria], quindi la funzione può alterare il contenuto cui punta il riferimento, modificando il valore della variabile esterna alla funzione. In Python si potrebbe dire che i parametri passati alle funzioni siano sempre trasferiti per riferimento, ma questa cosa va precisata. Questo si spiega con il fatto che le variabili sono in realtà delle etichette che si applicano agli oggetti in Python, quindi più vicini al concetto di riferimento che di variabile. In termini pratici possiamo rileggere il comportamento del nostro codice in questo modo: 1. assegniamo una etichetta y all’oggetto numero 4 2. chiamando la funzione assegniamo anche l’etichetta num all’oggetto numero 4 (come nei migliori passaggi per riferimento, ma poi cosa accade? ) 3. creiamo un oggetto 3+4 e gli assegnamo l’etichetta num (attenzione, abbiamo spostato l’etichetta num, ma l’etichetta y non è cambiata) 4. usciamo dalla funzione con l’etichetta y ancora ancorata all’oggetto 4 vediamo che succede alla lista: 1. assegnamo l’etichetta x all’oggetto lista [1,2] 2. nella funzione associamo anche l’etichetta lista all’oggetto lista [1,2] 3. a questo punto chiamiamo il metodo append dell’oggetto lista [1,2] e “accidentalmente” lo facciamo utilizzando l’etichetta lista. Il metodo modifica l’oggetto, ignorando la questione delle etichette 4. usciamo dalla funzione con l’etichetta x che punta all’oggetto lista [1,2,10] che è stato modificato Questo comportamento differente tra una lista e un oggetto numero, sta anche nel fatto che il primo è un tipo mutabile e il secondo no: le variabili di tipo mutabile possono cambiare di stato durante la loro vita, infatti per una lista è possibile aggiungere o togliere elementi in qualsiasi momento. per i tipi non mutabili ciò non è possibile, è possibile solamente cambiare in blocco l’intero valore. Parametri facoltativi È possibile introdurre dei parametri che hanno la caratteristica di essere facoltativi, essi assumono un valore prestabilito se non vengono valorizzati. Esaminiamo la sintassi di questo esempio: >>> def funzione2(a, b=30): print(a,b) >>> x=10 48 >>> y=19 >>> funzione2(x) 10 30 >>> funzione2(x,y) 10 19 La funzione denominata funzione2 ha due parametri: a e b. Essa viene invocata due volte: il risultato della prima chiamata sarà 10, 30, infatti il parametro b, non essendo indicato, assume il valore 30, il risultato della seconda chiamata sarà 10, 19, come ci si aspetta. Altri esempi Vediamo anche come passare in ingresso un insieme di valori e trasformarli in tuple (per mezzo dell’operatore * ) o in dizionari (con il doppio asterisco ** ) in modo automatico, vediamo come: >>> def creatupla(*tupla): print(tupla) print(type(tupla)) >>> creatupla('uno', 'due', 'tre') ('uno', 'due', 'tre') <class 'tuple'> Vediamo i dizionari: >>> def creadizionario(**dizionario): print(dizionario) print(type(dizionario)) >>> creadizionario(primo=1, secondo=2, terzo=3) {'terzo': 3, 'primo': 1, 'secondo': 2} <class 'dict'> 49 Moduli I moduli in Python non sono altro che file che possono contenere funzioni, classi. In altre parole sono librerie che ci permettono di organizzare meglio i progetti, specie quelli di grandi dimensioni e di riutilizzare il codice. I moduli quindi sono dei files di script (files con estensione “ .py“) che possono essere richiamati da altri programmi Python per riutilizzare le funzioni contenute in essi. Creare moduli è piuttosto semplice, ma ancor più interessante è conoscere quelli già pronti tra i moltissimi già a disposizione nell’installazione di Python oppure cercare quelli che si trovano online, messi a disposizione da terze parti. In “bundle” con Python abbiamo moduli che forniscono funzioni utili per risolvere diverse problematiche, come: gestione delle stringhe, chiamate alle funzioni del sistema operativo, gestione di internet, posta elettronica, … Online si trovano numerosissime librerie scritte da terzi per risolvere i problemi più disparati e molte di esse, in pieno stile open source, sono di utilizzo gratuito e sotto licenza GPL. Scrivere e importare un modulo Per scrivere un modulo, basta semplicemente creare un file con estensione .py e inserirvi tutte le funzioni necessarie. Per utilizzare il modulo appena creato è sufficiente importarlo con il comando import. Ad esempio, supponendo di voler utilizzare il modulo libreria.py, sarà sufficiente scrivere in cima al programma: import libreria Dopo aver importato il modulo possiamo semplicemente richiamare le funzioni contenute in esso utilizzando la dot notation. Digitando il nome del modulo, un punto e il nome della funzione riusciamo ad entrare nel modulo e richiamare la funzione desiderata. Ad esempio, supponendo che “libreria.py” contenga le funzioni apri(), sposta(oggetto), possiamo richiamare le funzioni nel seguente modo: libreria.apri() ogg=5 libreria.sposta(ogg) libreria.chiudi() 50 chiudi() e Se non si desidera utilizzare il nome della libreria tutte le volte che si richiama una funzione, è possibile importare anche i nomi delle funzioni direttamente. Ad esempio, per importare la funzione sposta(oggetto): import libreria from libreria import sposta libreria.apri() ogg=5 sposta(ogg) libreria.chiudi() In questo caso la funzione sposta viene a far parte dell’insieme dei nomi definiti nel programma (namespace), e non necessita più del prefisso del nome del modulo (libreria). Se si desidera importare tutti i nomi delle funzioni di “libreria.py” all’interno del programma basta utilizzare il carattere asterisco ( * ): from libreria import * apri() ogg=5 sposta(ogg) chiudi() Compilazione ottimizzata dei moduli Bisogna considerare un altro vantaggio offerto dai moduli: quando si interpreta un programma Python, esso crea per ogni modulo una versione “semicompilata” in un linguaggio intermedio (bytecode). Dopo una esecuzione, è possibile notare la presenza di tanti file con estensione .pyc (Python compiled). Ogni file ha lo stesso nome del relativo sorgente .py. In questo modo Python non dovrà interpretare tutto il codice tutte le volte, ma solamente quando viene effettuata una modifica. L’interprete confronta ogni volta la data e l’ora del file .py con il file .pyc: se sono diverse interpreta il codice, altrimenti esegue direttamente il bytecode. I moduli standard Python è dotato di una raccolta di moduli standard molto vasta. Per utilizzare tali moduli è sufficiente importarli come abbiamo visto in precedenza. Vediamo alcuni moduli di notevole importanza: 51 modulo string.py sys.py descrizione per gestire le stringhe, ad esempio: o funzioni per la conversione da stringa a numero o funzioni per la conversione da minuscolo a maiuscolo o funzioni per la sostituzioni di pezzi di stringhe per reperire informazioni di sistema, ad esempio reperire gli argomenti passati sulla linea di comando os.py per eseguire operazioni del sistema operativo sottostante, ad esempio copiare, rinominare o cancellare un file graphics.py per funzioni di grafica elementare Nella documentazione inclusa con Python si trova l’elenco completo di tutti i moduli predefiniti. Namespaces Per meglio comprendere il meccanismo dell’importazione dei nomi delle funzione bisogna parlare del concetto di namespace (termine già introdotto in precedenza): ogni modulo ha un insieme di nomi (nomi di variabili e di funzioni) che rappresentano i nomi utilizzabili al suo interno. Questo insieme viene detto namespace (spazio dei nomi). Per trasportare i nomi da un namespace ad un altro si può usare il comando import che abbiamo visto. Grazie alla funzione dir() è possibile visualizzare l’elenco dei nomi presenti nel namespace corrente. >>> dir() ['__builtins__', '__doc__', '__name__'] Questi nomi, mostrati sopra, sono nomi definiti dall’interprete prima di cominciare. Se io importo il modulo libreria ottengo il seguente risultato: >>> from libreria import * >>> dir() ['__builtins__', '__doc__', '__name__', 'apri', 'chiudi', 'sposta'] Come si intuisce, adesso tutte le funzioni contenute in libreria.py fanno parte del namespace corrente. 52 Il concetto di namespace si applica anche alle funzioni, quando si entra in una funzione viene creato un nuovo namespace, ecco perché non sono più visibili le variabili precedenti, ma solo le variabili locali alla funzione stessa. Infine vediamo altre due funzioni built-in: locals() e globals() >>> locals() {'__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__doc_ _': None, '__package__': None} >>> globals() {'__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__doc_ _': None, '__package__': None} Entrambe ritornano un dizionario, ma proviamo adesso ad importare un modulo: >>> import math >>> globals() {'__builtins__': <module 'builtins'="" (built-in)="">, '__name__': '__main__', '__doc_ _': None, 'math': <module 'math'="" (built-in)="">, '__package__': None} >>> locals() {'__builtins__': <module 'builtins'="" (built-in)="">, '__name__': '__main__', '__doc_ _': None, 'math': <module 'math'="" (built-in)="">, '__package__': None} </module></module></module></module> Come è possibile vedere il namespace globals e locals continuano ad essere equivalenti, ma con il nuovo modulo importato. 53 Gestione delle eccezioni Il momento peggiore per un programmatore è rappresentato dal blocco del programma a causa di un errore. Esistono due tipi di errori: Errori sintattici: dovuti all’errata scrittura del nome di un comando o di una funzione. Errori di runtime: dovuti al verificarsi di una condizione imprevista durante l’esecuzione (es. divisione per zero, lettura di file inesistenti, ... ) Quando si ha un errore, il programma si ferma e viene evidenziato un messaggio di errore. Questo evento provoca una brusca interruzione del flusso di controllo del programma, quindi non è più possibile mantenere in vita il programma. In realtà, ciò risulta vero per gli errori sintattici, per i quali l’interprete Python non sa cosa deve eseguire, ma non è completamente vero per gli errori runtime. Nel seguente esempio si ha un errore di runtime, in questo caso si dice che si è verificata una eccezione, ossia un evento accidentale (in realtà in questo esempio un po’ forzato …): >>> a, b = 5, 0 >>> print a / b traceback (most recent call last): File “<pyshell#5>”, line 1, in ? print a / b ZeroDivisionError: integer division or modulo by zero è possibile controllare questi eventi accidentali senza terminare il programma, per fare questo si utilizza un costrutto che permette di rilevare le eccezioni e passare il controllo di flusso ad un altro blocco di istruzioni: try: <gruppo di istruzioni sotto test> except: <gruppo di istruzioni da eseguire in caso di errore> else: # opzionale <gruppo di istruzioni2> finally: # opzionale, al posto di except <gruppo di istruzioni3> Tutte le istruzioni incluse nel blocco try sono tenute sotto controllo durante l’esecuzione del programma. Se tutto va bene il blocco except viene ignorato. In caso contrario, se avviene una eccezione, si salta al blocco di istruzione che seguono la parola except. Se incapsuliamo ogni operazione a rischio di eccezioni in un blocco try siamo in grado di reagire ad ogni errore runtime senza interrompere il programma. 54 Tornando all’esempio procedente si può operare in questo modo: >>> a, b = 5, 0 >>> try: print a / b except: print ‘tentativo di divisione per zero !’ Tentativo di di divisione per zero ! è possibile anche gestire tipologie diverse di eccezioni contemporaneamente, ed eseguire blocchi di istruzioni diverse a seconda del tipo di errore. Per permettere questo è necessario fare seguire alla parola except il nome della classe di errore. Tale nome è rilevabile ad esempio dal messaggio Python in caso di eccezione. Riportando il solito esempio, per gestire in modo più puntuale l’eccezione avremo potuto scrivere nel seguente modo: >>> a, b = 5, 0 >>> try: print a / b except ZeroDivisionError: print ‘tentativo di divisione per zero !’ Tentativo di di divisione per zero ! Nel caso si verificasse una eccezione non legata alla divisione per zero si verificherebbe ugualmente un blocco del programma, anche in presenza del blocco try. Se si desidera eseguire un particolare gruppo di istruzioni solo nel caso in cui non avvenga nessuna eccezione, basta inserire la parola chiave else, seguito dal blocco di istruzioni desiderate. Se si desidera far eseguire ugualmente il blocco except anche in caso di nessuna eccezione, è sufficiente sostituire la parola chiave except con la parola chiave finally. Una nota finale per chi volesse approfondire l’aspetto di gestione delle eccezioni di Python: è possibile creare delle classi di eccezioni personalizzate, le quali possono essere generate in un punto qualsiasi utilizzando il comando raise. 55 Classi e cenni di programmazione ad oggetti Nei capitoli introduttivi abbiamo detto che Python è un linguaggio orientato agli oggetti. In realtà esso permette sia la programmazione tradizionale (procedurale) che il nuovo paradigma ad oggetti. Quindi Python si inquadra nei linguaggi ibridi, come il C++. In questo capitolo cercheremo di introdurre teoricamente i concetti della programmazione orientata agli oggetti, accompagnando ogni concetto con qualche piccolo esempio. Solo in seguito trasferiremo i concetti teorici in Python, illustrando la sintassi necessaria all’utilizzo degli oggetti in Python stesso. La programmazione tradizionale si è sempre basata sull’utilizzo di strutture dati (come le liste, le tuple, … ) e su funzioni e procedure (tutti concetti già visti in precedenza). Questo metodo di sviluppo del software viene detto funzionale (o procedurale), con esso si organizza un intero programma in moduli che raccolgono gruppi di funzioni. Ogni funzione accede ad uno o più gruppi di dati. I metodi di sviluppo funzionale hanno però notevoli debolezze: A causa dei stretti legami tra le funzioni e i dati, si arriva ad un punto in cui ogni modifica software provoca degli effetti collaterali su altri moduli con enormi difficoltà di debug dell’applicazione. Difficoltà di riutilizzo del software. Ogni volta che si vuole riciclare una funzione bisogna apportare delle modifiche strutturali per adeguarla alla nuova applicazione. La programmazione orientata agli oggetti è un modo alternativo di scomposizione di un progetto software: in essa l’unità elementare di scomposizione non è più l’operazione (la procedura) ma l’oggetto, inteso come modello di un’entità reale (un oggetto del mondo reale). Questo approccio porta ad un modo nuovo di concepire un programma: il software è ora costituito da un insieme di entità (gli oggetti) interagenti, ciascuna provvista di una struttura dati e dell’insieme di operazioni che l’oggetto è in grado di effettuare su quella struttura. Poiché ciascun oggetto incapsula i propri dati e ne difende l’accesso diretto da parte del mondo esterno, si è certi che cambiamenti del mondo esterno non influenzeranno l’oggetto o il suo comportamento. D’altra parte per utilizzare un oggetto basta conoscere che dati esso immagazzina e che operazioni esso fornisce per operare su questi dati, senza curarsi dell’effettiva realizzazione interna dell’oggetto stesso. Questo nuovo modo di sviluppare programmi software è più vicino alla nostra realtà quotidiana. Pensiamo ad un oggetto del mondo reale, ad esempio una automobile. Una automobile ha un insieme di caratteristiche: il colore , la cilindrata ecc... Inoltre essa dispone di operazioni da svolgere esclusivamente con essa, ad esempio: accensione. cambio della marcia. parcheggio. è giusto correlare le sue caratteristiche e le sue operazioni in una sola entità (un solo oggetto). Inoltre la programmazione ad oggetti favorisce la programmazione di gruppo, poiché gli oggetti non possono dipendere dall’implementazione di altri oggetti, ma solo dalle loro operazioni, perciò 56 un programmatore può sviluppare un oggetto senza preoccuparsi della struttura degli altri elementi che compongono il sistema. Vediamo ora i principali concetti su cui si basa questo modo di programmare: Una classe definisce un tipo di oggetto. Se intendo creare un nuovo oggetto ( istanza ) devo indicare al programma le sue caratteristiche. In particolare la classe definisce: Le variabili contenute nell’oggetto, dette dati membri (o proprietà) Le funzioni contenute nell’oggetto, dette metodi. Queste funzioni permettono di svolgere operazioni solo sui dati membri dell’oggetto stesso. Ogni metodo agisce esclusivamente sui dati membri della classe di appartenenza. Questo vincolo rappresenta la grande forza della programmazione ad oggetti, la quale costringe il programmatore ad organizzare il software per componenti riciclabili ben distinti. Ad esempio, se volessi costruire un programma che conserva un archivio di persone, potrei creare la classe “PERSONA“. Tale classe potrebbe avere i seguenti dati membri: nome della persona cognome della persona indirizzo telefono stato civile Inoltre si potrebbero definire i seguenti metodi: cambia l’indirizzo della persona cambia il telefono della persona cambia lo stato civile della persona Questi metodi agiscono esclusivamente sui dati membri della classe. 57 Una volta definita una classe, siamo in grado di creare tanti oggetti distinti caratterizzati da differenti valori delle caratteristiche della classe stessa. Gli oggetti non sono altro che “istanze” della classe. Tornando al nostro esempio delle persone, possiamo creare i seguenti oggetti: Stefano Riccio Rossi Mario Elena Bianchi Queste sono tre istanze della classe persona. Ogni oggetto ha una copia dei dati membri dove conserva tutte le proprie informazioni. La definizione di classe permette di realizzare l’incapsulamento. Esso consiste nella protezione dei dati membri dagli accessi non desiderati. Questo avviene perché dall’esterno di un oggetto si può accedere ad un dato membro solo mediante un metodo e non direttamente usando il nome del dato membro stesso. Ad esempio, se volessi modificare l’indirizzo di una persona non posso semplicemente assegnare il nuovo indirizzo al nome della variabile (dato membro), come si farebbe nella programmazione tradizionale, ma sono obbligato ad utilizzare l’apposito metodo. In realtà l’incapsulamento non è obbligatorio. Si può decidere, singolarmente su ogni dato membro, se renderlo protetto oppure no. Generalmente gli oggetti hanno un insieme di metodi e dati che sono resi di dominio pubblico, mentre altri sono inaccessibili dall’esterno. Questo principio è conosciuto con il nome di “information hiding” (mascheramento dell’informazione) e fa si che un oggetto sia diviso in due parti ben distinte: una interfaccia pubblica e una rappresentazione privata. Il mascheramento dell’informazione permette di rimuovere dal campo di visibilità esterno all’oggetto alcuni metodi o dati che sono stati incapsulati nell’oggetto stesso. L’incapsulamento e il mascheramento dell’informazione lavorano insieme per isolare una parte del programma o del sistema dalle altre parti, permettendo così al codice di essere modificato ed esteso senza il rischio di introdurre indesiderati effetti collaterali. Questo metodo risulta molto utile per costruire librerie software da rilasciare a terze parti. Infatti, chi utilizza la libreria vede solamente l’interfaccia pubblica e ignora tutto il resto. 58 Un altro concetto importantissimo della programmazione ad oggetti e l’ereditarietà. Con essa è possibile definire una classe sfruttando le caratteristiche di un’altra classe (che diventa la classe madre). Ad esempio, se volessimo creare la classe degli studenti, dovremmo costruire una classe simile a quella delle persone, con l’aggiunta di qualche informazione in più. PERSONA STUDENTE Per evitare di scrivere tutto nuovamente, posso indicare al programma di ereditare tutte le caratteristiche dalla classe “persona” e aggiungere alcuni dati membri e alcuni metodi. In questo modo posso affermare che gli studenti sono delle persone, quindi ereditano da essi le loro caratteristiche. Inoltre posso aggiungere ad esempio i seguenti dati membri specifici degli studenti: scuola di appartenza classe di appartenza Inoltre si potrebbero definire i seguenti metodi specifici della classe studenti: cambia scuola promosso L’ereditarietà permette di utilizzare anche i metodi delle classi progenitrici, cosi per cambiare il nome di uno studente posso semplicemente utilizzare il metodo apposito della classe persona. Un metodo, in una classe discendente, può nascondere la visibilità di un metodo di una classe antenata, semplicemente definendo il metodo con lo stesso nome. Lo stesso nome può così essere utilizzato in modo appropriato per oggetti che sono istanze di classi diverse. Ereditando dalle classi antenate i metodi che operano correttamente anche nelle classi discendenti, ed eliminando quei metodi che devono agire in modo differente, il programmatore estende realmente una classe antenata senza doverla completamente ricreare. La forma più comune di creazione di una classe è la specializzazione, mediante la quale viene creata una nuova classe poiché quella esistente è troppo generica. In questo caso è possibile utilizzare il meccanismo dell’ereditarietà singola. Questo è il caso dell’esempio proposto delle persone e degli studenti. Un altro tipico metodo di creazione è la combinazione con la quale la nuova classe è creata combinando gli oggetti di altre classi. In questo caso, se esiste, si utilizza il meccanismo dell’ereditarietà multipla. 59 Per porre in relazione gli oggetti, sono quindi utilizzati due tipi di ereditarietà: quella singola e quella multipla. Attraverso l’ereditarietà singola, una sottoclasse può ereditare i dati membri ed i metodi da un’unica classe, mentre con l’ereditarietà multipla una sottoclasse può ereditare da più di una classe. L’ereditarietà singola procura i mezzi per estendere o per perfezionare le classi, mentre l’ereditarietà multipla fornisce in più i mezzi per combinare o unire classi diverse. Le opinioni riguardo la necessità o meno del meccanismo dell’ereditarietà multipla sono piuttosto controverse. I contrari sostengono che si tratta di un meccanismo non strettamente necessario, piuttosto complesso e difficile da utilizzare correttamente. Quelli a favore dicono esattamente l’opposto ed infatti sostengono che l’ereditarietà multipla è una caratteristica fondamentale di un linguaggio object oriented. L’ereditarietà multipla incrementa sicuramente le capacità espressive di un linguaggio, ma comporta un costo elevato in termini di complessità della sintassi e di overhead di compilazione e di esecuzione e, in definitiva, risulta di scarso utilizzo pratico. I linguaggi object-oriented permettono la creazione di gerarchie di oggetti (mediante l’ereditarietà) cui sono associati metodi che hanno lo stesso nome, per operazioni che sono concettualmente simili ma che sono implementate in modo diverso per ogni classe della gerarchia. Di conseguenza, la stessa funzione richiamata su oggetti diversi, può causare effetti completamente differenti. Questa funzionalità viene chiamata polimorfismo. Un caso molto interessante di polimorfismo è rappresentato dall’overloading degli operatori. Con questa tecnica è possibile definire degli operatori matematici sugli oggetti che permettono di compiere operazioni sulle istanze in base al tipo di oggetto con il quale lavorano. Approfondiremo questo aspetto in seguito. 60 Classi in Python Ecco di seguito la sintassi da utilizzare per definire una classe in Python: class <nome classe> [(<classe madre>,...)]: <elenco dati membro da inizializzare> <elenco metodi> I dati membro si esprimono come le normali variabili di Python. Se devo inizializzare dei dati membro faccio un semplice assegnamento, altrimenti posso definirli al momento dell’utilizzo, esattamente come avviene per le variabili normali. Per i metodi basta utilizzare la medesima sintassi delle funzioni con alcuni accorgimenti. Ogni metodo deve avere come primo parametro l’oggetto stesso, infatti ogni volta che viene richiamato il metodo, Python sistema nel primo parametro il riferimento all’oggetto stesso. Questo permette di accedere ai dati membri appartenenti allo stesso oggetto, normalmente si usa chiamare questo parametro “self“. Vediamo l’esempio delle persone scritto praticamente in codice Python: class persona: nome = '' cognome = '' indirizzo = '' telefono = '' stato_civile = '' def cambia_indirizzo(self,s): self.indirizzo = s def cambia_telefono(self,s): self.telefono = s def cambia_stato_civile(self,s): self.stato_civile = s def stampa(self): print (self.nome,self.cognome,self.indirizzo,self.stato_civile) class studente (persona): # ereditarietà scuola = '' classe = 0 def cambia_scuola(self,s): self.scuola = s def promosso(self): if self.classe == 5: print ('scuola finita') else: self.classe = self.classe + 1 Se si vuole istanziare un oggetto, è sufficiente richiamare il nome della classe come se fosse una funzione. Per accedere ai dati membri e ai metodi basta utilizzare il punto. 61 p1 = persona() # le parentesi sono obbligatorie p1.nome = 'Pico' # assegnazione diretta ai dati membri p1.cognome = 'De Paperis' p1.cambia_indirizzo('via dei ciclamini n. 12') #richiamo un metodo p1 è un oggetto della classe persona che contiene quei valori. Si noti come l’assegnazione dei valori ai dati membri venga effettuata direttamente oppure attraverso gli appositi metodi. L’utilizzo del metodo cambia_indirizzo passando un solo parametro (il metodo ne chiede due) è reso possibile perché Python associa al parametro self l’oggetto p1. Utilizzando self si può accedere direttamente ai dati membro all’interno della classe. Per istanziare un oggetto di tipo studente, è possibile utilizzare gli stessi dati membri e metodi della classe progenitrice: s1 = studente() s1.nome = 'mario' s1.cognome = 'rossi' s1.cambia_indirizzo('via html n. 10') s1.cambia_scuola('liceo') s1.stampa() # risultato : mario rossi via html n. 10 Il risultato del metodo “stampa” non permette di visualizzare la scuola e la classe di appartenenza. Questo avviene poiché viene richiamato il metodo della classe “persona” (classe madre), la quale non può conoscere il valore dei dati membri della classe “studente” (classe figlia). Per risolvere questo problema ci viene in aiuto il polimorfismo, infatti è necessario ridefinire il metodo stampa costruendone una versione specifica per gli studenti. Riscrivo la classe studente in questo modo: class studente (persona): scuola = '' classe = 0 def cambia_scuola(self,s): self.scuola = s def promosso(self): if self.classe == 5: print ('scuola finita') else: self.classe = self.classe + 1 def stampa(self): print self.nome,self.cognome,self.indirizzo,self.stato_civile print 'scuola: '+self.scuola+' classe '+str(self.classe) 62 Il metodo stampa è polimorfico, sembra una brutta parola, ma questo significa che assume una forma diversa in base al tipo di oggetto sul quale viene applicato. Spesso è necessario inizializzare i dati membri dell’oggetto nel momento della creazione dell’istanza dell’oggetto stesso. Nel nostro esempio è utile inizializzare subito la persona con il suo nome e cognome (infatti il nome e cognome sono assegnati alla nascita e non cambiano più). Per fare questo la teoria della programmazione orientata agli oggetti prevede l’uso di una funzione costruttore. In Python esiste qualcosa di simile: è la funzione denominata “__init__()”. Quando all’interno di una classe viene dichiarata una funzione con questo nome, allora essa viene invocata automaticamente alla creazione dell’oggetto. Vediamo come modificare la classe “persona” per inizializzare automaticamente il nome e il cognome: class persona: indirizzo = '' telefono = '' stato_civile = '' def __init__(self,n,c): self.nome = n self.cognome = c def cambia_indirizzo(self,s): self.indirizzo = s def cambia_telefono(self,s): self.telefono = s def cambia_stato_civile(self,s): self.stato_civile = s def stampa(self): print (self.nome,self.cognome,self.indirizzo,self.stato_civile) Con la chiamata: p1 = persona('Paolino','Paperino') p1.stampa() # risultato : Paolino Paperino Si ottiene l’inizializzazione dei due dati membri. Da notare che non è stata eliminata l’assegnazione della stringa vuota alle due variabili nome e cognome nella classe: esse infatti vengono direttamente create all’interno della funzione __init__. In precedenza abbiamo parlato di incapsulamento e subito dopo non abbiamo applicato questo principio. Infatti la prima operazione fatta sull’oggetto p1 è stata quella di inizializzare i dati membri nome e cognome direttamente. 63 Python assume che tutti i dati membri siano pubblici e quindi modificabili dall’esterno dell’oggetto. Per indicare a Python di tenere protetti i dati membri, e quindi realizzare veramente l’incapsulamento, si devono anteporre 2 underscore al nome della variabile (“__”). Cosi facendo nessuno può modificare il dato senza utilizzare l’apposita funzione. Ad esempio, nella classe “persona” volendo proteggere tutti i dati membri: class persona: __indirizzo = '' __telefono = '' __stato_civile = '' def __init__(self,n,c): self.__nome = n self.__cognome = c def cambia_indirizzo(self,s): self.__indirizzo = s def cambia_telefono(self,s): self.__telefono = s def cambia_stato_civile(self,s): self.__stato_civile = s def stampa(self): print (self.__nome,self.__cognome,self.__indirizzo, self.__stato_civile) Se tento di assegnare direttamente “__nome” o “__cognome” il comando viene ignorato: p1.__nome = 'prova' p1.stampa() # il risultato rimane quello dell’ultima assegnazione Esiste un altro concetto della programmazione orientata agli oggetti molto interessante: la possibilità di effettuare il polimorfismo anche degli operatori, oltre che dei metodi. Supponiamo di creare la classe dei numeri complessi nel seguente modo: class numero_complesso: parte_reale = 0 parte_imm = 0 def somma(self,num): # num è un oggetto di classe numero_complesso self.parte_reale = self.parte_reale + num.parte_reale self.parte_imm = self.parte_imm + num.parte_imm def stampa(self): print (str(self.parte_reale) + '+' + str(self.parte_imm)+ 'i') 64 Un oggetto di classe numero_complesso è composto da due valori: la parte reale e la parte immaginaria e possiede un metodo per eseguire la somma con un altro oggetto della stessa classe. Per utilizzare questa classe devo scrivere il seguente programma: n1 = numero_complesso() n1.parte_reale = 1 n1.parte_immaginaria = 1 n2 = numero_complesso() n2.parte_reale = 2 n2.parte_immaginaria = 3 n1.somma(n2) n1.stampa() # risultato 3+4i La sintassi, purtroppo, risulta piuttosto articolata: sarebbe molto più intuitivo e pratico poter scrivere n1 + n2, proprio come si fa in matematica. Per poter scrivere questo è necessario rendere l’operatore “+” di Python polimorfico, in modo che si accorga di lavorare con i numeri complessi e richiami la giusta funzione. Per fare questo si deve dichiarare la classe nel seguente modo: class numero_complesso: parte_reale = 0 parte_imm = 0 def __add__(self,num): ris=numero_complesso() ris.parte_reale = self.parte_reale + num.parte_reale ris.parte_imm = self.parte_imm + num.parte_imm return ris def stampa(self): print (str(self.parte_reale) + '+' + str(self.parte_imm)+ 'i') La dichiarazione della funzione speciale “__add__” effettua l’overloading (la sostituzione) dell’operatore ADD (+), creando al suo interno un nuovo oggetto ed eseguendo somma delle singole variabili. Vediamo la nuova sintassi di chiamata della funzione: n1 = numero_complesso() n1.parte_reale = 1 n1.parte_immaginaria = 1 n2 = numero_complesso() n2.parte_reale = 2 n2.parte_immaginaria = 3 r=n1+n2 # come in matematica n1.stampa() # risultato 3+4i 65 Attraverso l’overloading degli operatori si possono creare classi molto sofisticate utilizzabili con una sintassi veramente molto elegante. Si riporta di seguito una tabella con alcune funzioni speciali con le quali è possibile effettuare l’overloading: Metodi speciali Descrizione Esempio __init__ costruttore oggetto P1=persona() __add__ operatore di somma n1+n2 __or__ operatore OR x ¦y __len__ lunghezza len(x) __cmp__ confronto x==y I sorgenti Python sono raggruppati in moduli. Anche i moduli, come le classi, sono uno strumento per organizzare bene un programma e ottenere una componentistica da riutilizzare in futuro. Si pone il problema di come organizzare le classi nei moduli: benché sia possibile inserire più classi nello stesso modulo, si consiglia di utilizzare un modulo per ogni classe. In questo modo i singoli file sul disco equivalgono alle classi implementate e si ottiene un miglior ordine del programma. Una considerazione finale prima di concludere l’argomento della programmazione ad oggetti. è difficile esaurire l’argomento della programmazione ad oggetti, poiché esso è molto vasto e articolato. Bisogna tenere in considerazione che ancora oggi è difficile programmare bene ad oggetti per diversi motivi: Ogni linguaggio di programmazione, compreso Python, implementa in modo diverso e solo in parte le linee teoriche della programmazione ad oggetti. La maggior parte dei linguaggi sono ibridi e quindi non obbligano il programmatore ad usare correttamente gli oggetti. Non è semplice modellare un problema della vita reale ad oggetti per trasformalo in un programma. Python è un linguaggio ibrido, e quindi lascia libero il programmatore di scegliere se utilizzare o meno gli oggetti. Questo potrebbe essere un punto a sfavore di Python rispetto ad altri linguaggio object orientd puri, come java per esempio. 66 APPENDICE Colorconsole Colorconsole è un modulo per la gestione della modalità testo in applicazioni console per Windows, Linux and Mac OS X. ! Lo script deve essere eseguito dalla console di Windows (previa impostazione del percorso dell’interprete python.exe nella variabile d’ambiente path) Installazione Copiare la cartella colorconsole in App\Lib Testarne il funzionamento con: import colorconsole.terminal colorconsole.terminal.test() Tabella colori Numero 0 1 2 3 4 5 6 7 Colore Black Blue Green Cyan Red Purple Brown Light grey Numero 8 9 10 11 12 13 14 15 Colore Dark grey Light blue Light Green Light Cyan Light Red Light Purple Yellow White Metodi dell’oggetto terminal Al momento sono supportati i terminali Windows e Ansi. Metodo set_color(fg = None, bk = None) set_title(titolo) cprint(fg, bg, testo) print_at(x,y, testo) clear() gotoXY(x,y) reset() columns() lines() Descrizione Imposta i colori di primo piano(fg) e di sfondo (bg) Imposta il titolo della finestra console Stampa un messaggio con i colori di testo (fg) e sfondo (bg) specificati. Equivale all’utilizzo congiunto di set_color e print Stampa il testo alle coordinate x,y Cancella lo schermo utilizzando il colore di sfondo corrente. Non reimposta la posizione del cursore a (0,0) Imposta la posizione del cursore alla colonna x e alla riga y Resetta tutti gli attributi di colore Ritorna il numero di colonne della finestra di console corrente Ritorna il numero di righe della finestra di console corrente 67 La grafica Esistono varie librerie di grafica, ma una delle più semplici e intuitive per programmare con le funzioni grafiche fondamentali , è graphics.py La libreria consente la creazione di finestre (GraphWin) su cui è possibile disegnare tutta una serie di oggetti grafici elementari (punti, linee, cerchi, rettangoli, ovali, poligoni, testo, immagini) . Ecco un semplice esempio: from graphics import * def main(): win = GraphWin("My Circle", 100, 100) c = Circle(Point(50,50), 10) c.draw(win) win.getMouse() # attesa click nella finestra win.close() main() GraphWin In un programma si può specificare un qualsiasi numero di finestre grafiche Metodo GraphWin(titolo, largh, alt) plot(x, y, colore) plotPixel(x, y, Color) setBackground(color) close() getMouse() checkMouse() setCoords(xbs, ybs, xad, yad) 68 Descrizione Crea una nuova finestra. I parametri sono opzionali. ( default: Graphic Window, 200, 200 ) Disegna un pixel alle coordinate x e y col colore specificato. Il colore è opzionale ( default: nero ) Disegna un pixel ignorando le trasformazioni di coordinate Imposta il colore di sfondo della finestra. ( default: grigio ) Chiude la finestra Attesa di click del mouse all’interno della finestra. Le coordinate vengono restituite come oggetto Point Simile al precedente, senza attesa. Restituisce l’ultimo punto di click o None se non vi sono stati click dall’ultima chiamata di checkMouse o getMouse. Imposta il sistema di coordinate della finestra (angolo basso-sinistro, angolo alto-destro). Tutti i successivi tracciamenti seguono il nuovo sistema di coordinate (ad eccezione di plotPixel). Oggetti grafici La libreria implementa le seguenti classi di oggetti grafici: Point Line Circle Oval Rectangle Polygon Text Tutti gli oggetti sono creati inizialmente vuoti, con colore del tratto nero e supportano i seguenti metodi generici: Metodo setFill(colore) setOutline(colore) setWidth(pixel) draw(finestra) undraw() move(dx,dy) clone() Descrizione Imposta il colore dell’area interna dell’oggetto al colore specificato Imposta il colore del contorno dell’oggetto al colore specificato. Imposta lo spessore del contorno al numero di pixel specificato (non applicabile per l’oggetto Point) Disegna l’oggetto nella finestra specificata Cancella l’oggetto dalla finestra specificata. Causa errore per oggetti non disegnati. Sposta l’oggetto di dx unità in direzione x e di dy unità in direzione y. Se l’oggetto è già tracciato, l’immagine viene aggiornata alla nuova posizione Restituisce una copia non visibile dell’oggetto L’oggetto Point Metodo Point(x,y) getX() getY() Descrizione Costruisce un punto alle coordinate specificate Restituisce la coordinata x di un punto Restituisce la coordinata y di un punto L’oggetto Line Metodo Line(punto1,punto2) setArrow(punta) getCenter() getP1(), getP2() Descrizione Costruisce un segmento fra i due punti specificati Imposta una freccia con punte assegnate. Possibili valori di punta: 'first', 'last', 'both', e 'none' ( default: 'none' ) Restituisce una copia del punto medio del segmento Restituisce una copia degli estremi del segmento 69 L’oggetto Circle Metodo Circle(centro,raggio) getCenter() getRadius() getP1(), getP2() Descrizione Costruisce un cerchio con centro e raggio assegnati Restituisce una copia del centro del cerchio Restituisce il raggio del cerchio Restituisce una copia dei vertici del quadrato circoscritto L’oggetto Rectangle Metodo Rectangle(punto1,punto2) getCenter() getP1(), getP2() Descrizione Costruisce un rettangolo con vertici opposti nei punti assegnati Restituisce una copia del punto centrale del rettangolo Restituisce una copia dei punti utilizzati per definire il rettangolo L’oggetto Oval Metodo Oval(punto1,punto2) getCenter() getP1(), getP2() Descrizione Costruisce un ovale all’interno del rettangolo con vertici opposti nei punti assegnati Restituisce una copia del punto centrale dell’ovale Restituisce una copia dei punti utilizzati per definire l’ovale L’oggetto Polygon Metodo Polygon(punto1,punto2, ...) getCenter() getPoints() Descrizione Costruisce un poligono avente come vertici i punti assegnati. I vertici possono anche essere definiti per mezzo di una lista di punti Restituisce una copia del punto centrale dell’ovale Restituisce una lista di punti duplicati dei vertici L’oggetto Text Metodo Text(punto, testo) setText(testo) getText() getAnchor() setFace(tipo) setSize(dimensioni) setStyle(style) setTextColor(colore) 70 Descrizione Costruisce un oggetto Text che visualizza il testo specificato centrato sul punto centro. L’orientamento del testo è orizzontale Imposta il testo dell’oggetto Restituisce il testo corrente Restituisce un duplicato del punto centrale Imposta il tipo di font. Valori possibili: 'helvetica', 'courier', 'times roman', 'arial' Imposta le dimensioni del font. Valori possibili: da 5 a 36 Imposta lo stile del font. Valori possibili: 'normal', 'bold', 'italic', 'bold italic' Imposta il colore del testo. (In alternativa setFill) L’oggetto Entry Gli oggetti di tipo Entry sono visualizzati come caselle di inserimento testo editabili dall’utente del programma e supportano i metodi grafici generici move(), draw(finestra), undraw(), setFill(colore), clone() Metodo Entry(punto, larghezza) getAnchor() getText() setText(testo) setFace(tipo) setSize(dimensioni) setStyle(style) setTextColor(colore) Descrizione Costruisce un oggetto Entry con centro e larghezza assegnati. La larghezza rappresenta il numero di caratteri del testo che possono essere visualizzati Restituisce un duplicato del punto centrale della casella Restituisce il testo corrente nella casella Imposta il testo della casella. Imposta il tipo di font. Valori possibili: 'helvetica', 'courier', 'times roman', 'arial' Imposta le dimensioni del font. Valori possibili: da 5 a 36 Imposta lo stile del font. Valori possibili: 'normal', 'bold', 'italic', 'bold italic' Imposta il colore del testo. L’oggetto Image La libreria graphics fornisce, attraverso l’oggetto Image, le funzioni base per la visualizzazione ed elaborazione elementare di immagini. La maggior parte delle piattaforme supporta almeno i formati gif e ppm. L’oggetto Image implementa i metodi generici move(dx,dy), draw(finestra), undraw(), and clone() Metodo Image(punto, nomeFile) getAnchor() getWidth() getHeight() getPixel(x,y) setPixel(x,y,colore) save(nomeFile) Descrizione Costruisce un’immagine centrata su punto, col contenuto del file specificato. Se al posto del file si specificano larghezza e altezza, viene creata un immagine trasparente Restituisce un duplicato del punto centrale della casella Restituisce la larghezza dell’immagine Restituisce l’altezza dell’immagine Restituisce una lista [red, green, blue]con i valori rgb del pixel in posizione (x,y). Si noti che la posizione del pixel è relativa all’immagine stessa e non alla finestra. L’angolo superiore sinistro dell’immagine ha coordinate (0,0) Imposta il pixel in posizione x,y col colore specificato (è un’operazione lenta) Salva l’immagine in un file. Il tipo di file è determinato dall’estensione (ppm, gif) 71 I colori I colori più frequenti sono indicati da stringhe: 'red', 'purple', 'green', 'cyan', ... E’ possibile specificare gradazioni crescenti di scuro attraverso un numero (es. 'red1','red2','red3',...) E’ anche possibile comporre i colori specificando numericamente (0..255) le percentuali di rosso, verde e blu tramite la funzione color_rgb(red, green, blue). L’aggiornamento della finestra grafica Di solito la finestra grafica viene aggiornata ogni qual volta si richiama il metodo draw, oppure cambia lo stato di visualizzazione di un oggetto. Tuttavia, in alcune situazioni, può rendersi necessario forzare l’aggiornamento della finestra per rendere visibili i cambiamenti. A tal fine si utilizza la funzione update()che causa il completamento di tutte le operazioni di grafica pendenti. All’opposto, per ragioni di efficienza, si può desiderare talvolta di disattivare l’aggiornamento automatico della finestra ad ogni cambiamento degli oggetti grafici in essa contenuti. Per esempio, nelle animazioni, è preferibile cambiare l’aspetto di più oggetti prima di passare alla visualizzazione del fotogramma successivo. Per controllare la modalità di aggiornamento, il costruttore della finestra grafica include un parametro aggiuntivo chiamato autoflush. Per default, tale parametro è attivo. Se si desidera disattivarlo in modo che l’aggiornamento non avvenga automaticamente, lo si deve impostare a False durante la costruzione della finestra grafica Win = GraphWin("animazione",400,400,autoflush=False) In tal modo i cambiamenti agli oggetti grafici verranno mostrati durante gli intervalli di inattività del sistema o tramite chiamata esplicita della funzione update(). 72