diff options
Diffstat (limited to 'pypers/marelli/materiale/corso.html')
-rwxr-xr-x | pypers/marelli/materiale/corso.html | 1850 |
1 files changed, 1850 insertions, 0 deletions
diff --git a/pypers/marelli/materiale/corso.html b/pypers/marelli/materiale/corso.html new file mode 100755 index 0000000..8a0de82 --- /dev/null +++ b/pypers/marelli/materiale/corso.html @@ -0,0 +1,1850 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.3.7: http://docutils.sourceforge.net/" /> +<title>Corso Python Magneti Marelli</title> +</head> +<body> +<div class="document" id="corso-python-magneti-marelli"> +<h1 class="title">Corso Python Magneti Marelli</h1> +<table class="docinfo" frame="void" rules="none"> +<col class="docinfo-name" /> +<col class="docinfo-content" /> +<tbody valign="top"> +<tr class="field"><th class="docinfo-name">Tenuto:</th><td class="field-body">19-23 Settembre 2005</td> +</tr> +<tr class="field"><th class="docinfo-name">Autore:</th><td class="field-body">Michele Simionato</td> +</tr> +<tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference" href="mailto:michele.simionato@gmail.com">michele.simionato@gmail.com</a></td> +</tr> +</tbody> +</table> +<p><em>Queste dispense sono un compendio informale e molto sintetico di quanto +svolto durante il corso. Esse non si pongono in nessun modo come un testo +sistematico di programmazione in Python. Il loro scopo principale è quello +di tenere traccia, per quanto possibile, di quanto si è detto. Lo +scopo secondario è quello di invogliare i partecipanti e tutti +gli eventuali lettori ad affrontare gli argomenti qui trattati per forza +di cose in maniera abbozzata, in maniera più sistematica andando a +consultare le fonti più adatte alla bisogna, siano essi libri di testo, +la documentazione ufficiale, newsgroups, siti Web dedicati e, in ultima +instanza, il codice sorgente stesso, l'unico riferimento definitivo.</em></p> +<div class="contents topic" id="contents"> +<p class="topic-title first"><a name="contents">Indice</a></p> +<ul class="simple"> +<li><a class="reference" href="#i-messaggi-fondamentali-del-corso" id="id1" name="id1">I messaggi fondamentali del corso</a><ul> +<li><a class="reference" href="#studiare-paga" id="id2" name="id2"><em>Studiare paga</em></a></li> +<li><a class="reference" href="#disaccoppiamento-e-kiss" id="id3" name="id3"><em>Disaccoppiamento</em> e <em>KISS</em></a></li> +<li><a class="reference" href="#non-nascondete-gli-errori-sotto-il-tappeto" id="id4" name="id4"><em>Non nascondete gli errori sotto il tappeto</em></a></li> +</ul> +</li> +<li><a class="reference" href="#modulo-1-strumenti-di-introspezione-sviluppo-e-debugging" id="id5" name="id5">Modulo 1: Strumenti di introspezione, sviluppo e debugging</a><ul> +<li><a class="reference" href="#strumenti-di-introspezione-e-come-ottenere-aiuto" id="id6" name="id6">Strumenti di introspezione e come ottenere aiuto</a></li> +<li><a class="reference" href="#ambienti-di-sviluppo" id="id7" name="id7">Ambienti di sviluppo</a></li> +<li><a class="reference" href="#strumenti-di-debugging" id="id8" name="id8">Strumenti di debugging</a></li> +<li><a class="reference" href="#strumenti-utili-per-l-utenza-scientifica-ingegneristica" id="id9" name="id9">Strumenti utili per l'utenza scientifica/ingegneristica</a></li> +</ul> +</li> +<li><a class="reference" href="#modulo-2-programmazione-di-base-in-python" id="id10" name="id10">Modulo 2: Programmazione di base in Python</a><ul> +<li><a class="reference" href="#encoding" id="id11" name="id11">Encoding</a></li> +<li><a class="reference" href="#pathnames" id="id12" name="id12">Pathnames</a></li> +<li><a class="reference" href="#insiemi" id="id13" name="id13">Insiemi</a></li> +<li><a class="reference" href="#differenza-tra-mutabili-e-immutabili" id="id14" name="id14">Differenza tra mutabili e immutabili</a></li> +<li><a class="reference" href="#getattr-e-setattr" id="id15" name="id15">getattr e setattr</a></li> +<li><a class="reference" href="#gestione-dei-processi" id="id16" name="id16">Gestione dei processi</a></li> +<li><a class="reference" href="#iteratori-e-generatori" id="id17" name="id17">Iteratori e generatori</a></li> +</ul> +</li> +<li><a class="reference" href="#modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni" id="id18" name="id18">Modulo 3. Tipici errori di programmazione e gestione delle eccezioni</a><ul> +<li><a class="reference" href="#l-errore-pi-tipico-con-le-eccezioni" id="id19" name="id19">L'errore più tipico con le eccezioni</a></li> +<li><a class="reference" href="#il-try-finally-una-grande-idea" id="id20" name="id20">Il try .. finally è una grande idea</a></li> +<li><a class="reference" href="#uso-di-assert" id="id21" name="id21">Uso di assert</a></li> +<li><a class="reference" href="#non-usate-exec" id="id22" name="id22">Non usate exec</a></li> +<li><a class="reference" href="#come-far-partire-pdb-automaticamente-in-caso-di-eccezioni-inaspettate" id="id23" name="id23">Come far partire pdb automaticamente in caso di eccezioni inaspettate</a></li> +<li><a class="reference" href="#eccezioni-e-threads" id="id24" name="id24">Eccezioni e threads</a></li> +</ul> +</li> +<li><a class="reference" href="#modulo-4-sviluppo-orientato-ai-test" id="id25" name="id25">Modulo 4. Sviluppo orientato ai test</a><ul> +<li><a class="reference" href="#consigli-di-carattere-generale" id="id26" name="id26">Consigli di carattere generale</a></li> +<li><a class="reference" href="#usare-unittest" id="id27" name="id27">Usare unittest</a></li> +<li><a class="reference" href="#usare-doctest" id="id28" name="id28">Usare doctest</a></li> +<li><a class="reference" href="#un-esempio-di-programma-sviluppato-in-maniera-incrementale" id="id29" name="id29">Un esempio di programma sviluppato in maniera incrementale</a></li> +</ul> +</li> +<li><a class="reference" href="#modulo-5-design-documentazione-e-manutenzione-di-librarie" id="id30" name="id30">Modulo 5: Design, documentazione e manutenzione di librarie</a><ul> +<li><a class="reference" href="#la-filosofia-del-python" id="id31" name="id31">La filosofia del Python</a></li> +<li><a class="reference" href="#principio-del-disaccoppiamento" id="id32" name="id32">Principio del disaccoppiamento</a></li> +<li><a class="reference" href="#principio-del-kiss" id="id33" name="id33">Principio del KISS</a></li> +<li><a class="reference" href="#importanza-di-avere-un-prototipo" id="id34" name="id34">Importanza di avere un prototipo</a></li> +<li><a class="reference" href="#moduli-e-packages" id="id35" name="id35">Moduli e packages</a></li> +<li><a class="reference" href="#come-si-documenta-una-libreria-python" id="id36" name="id36">Come si documenta una libreria Python</a></li> +</ul> +</li> +<li><a class="reference" href="#modulo-6-domande-estemporanee" id="id37" name="id37">Modulo 6: Domande estemporanee</a><ul> +<li><a class="reference" href="#come-funziona-import" id="id38" name="id38">Come funziona 'import'</a></li> +<li><a class="reference" href="#come-funziona-del" id="id39" name="id39">Come funziona '__del__'</a></li> +<li><a class="reference" href="#che-differenza-c-fra-variabili-di-classe-e-di-istanza" id="id40" name="id40">Che differenza c'è fra variabili di classe e di istanza</a></li> +<li><a class="reference" href="#che-cosa-sono-i-metodi-che-iniziano-con" id="id41" name="id41">Che cosa sono i metodi che iniziano con "__"</a></li> +</ul> +</li> +<li><a class="reference" href="#soluzioni-al-questionario-di-ammissione" id="id42" name="id42">Soluzioni al questionario di ammissione</a><ul> +<li><a class="reference" href="#scrivere-un-programma-che-testa-se-una-stringa-rappresenta-un-numero" id="id43" name="id43">Scrivere un programma che testa se una stringa rappresenta un numero</a></li> +<li><a class="reference" href="#scrivere-un-programma-che-lista-tutti-i-files-nella-directory-corrente" id="id44" name="id44">Scrivere un programma che lista tutti i files nella directory corrente</a></li> +<li><a class="reference" href="#listare-tutti-i-files-nelle-sottodirectories-ricorsivamente" id="id45" name="id45">Listare tutti i files nelle sottodirectories ricorsivamente</a></li> +<li><a class="reference" href="#calcolare-lo-spazio-occupato-da-tutti-i-files-di-tipo-txt-in-una-directory" id="id46" name="id46">Calcolare lo spazio occupato da tutti i files di tipo .txt in una directory</a></li> +<li><a class="reference" href="#listare-i-files-a-seconda-delle-dimensioni" id="id47" name="id47">Listare i files a seconda delle dimensioni</a></li> +<li><a class="reference" href="#scrivere-un-test-per-verificate-che-una-directory-sia-vuota" id="id48" name="id48">Scrivere un test per verificate che una directory sia vuota</a></li> +<li><a class="reference" href="#aprire-una-finestrella-contenente-la-scritta-hello-world" id="id49" name="id49">Aprire una finestrella contenente la scritta "hello, world!"</a></li> +<li><a class="reference" href="#scaricare-la-pagina-web-http-www-example-com-da-internet" id="id50" name="id50">Scaricare la pagina Web http://www.example.com da Internet</a></li> +<li><a class="reference" href="#stampare-a-schermo-una-tavola-delle-moltiplicazioni" id="id51" name="id51">Stampare a schermo una tavola delle moltiplicazioni</a></li> +<li><a class="reference" href="#trovare-tutte-le-immagini-jpg-nel-vostro-hard-disk-e-mostrarle-a-schermo-in-formato-ridotto" id="id52" name="id52">Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo in formato ridotto</a></li> +</ul> +</li> +</ul> +</div> +<div class="section" id="i-messaggi-fondamentali-del-corso"> +<h1><a class="toc-backref" href="#id1" name="i-messaggi-fondamentali-del-corso">I messaggi fondamentali del corso</a></h1> +<div class="section" id="studiare-paga"> +<h2><a class="toc-backref" href="#id2" name="studiare-paga"><em>Studiare paga</em></a></h2> +<p>Grazie alla sua macchina del tempo, Guido ha risolto i problemi +che vi stanno affliggendo ora dieci anni fa, e sono già nel linguaggio +e nella libreria standard. Per esempìo avete scoperto di avere a +disposizione:</p> +<ul class="simple"> +<li>le stringhe di documentazione per darvi la documentazione automatica +del codice tramite strumenti quali pydoc ed altri;</li> +<li>il try ... finally per garantirvi un "gracefull exit" del vostro +programma, anche in presenza di eccezioni inaspettate;</li> +<li>i metodi setUp e tearDown di unittest.TestCase, che vi permettono +di inizializzare e di pulire propriamente l'ambiente per ogni test;</li> +<li>sia unittest che doctest vi permetto di gestire le eccezioni (nel +senso di gestire le eccezioni "buone", quelle che vi aspettate);</li> +<li>call e Popen in subprocess per aprire processi, kill per ucciderli +(usando win32api.TerminateProcess);</li> +<li>twisted vi gestisce la comunicazione tra processi e tutte le eccezioni +senza problemi</li> +</ul> +<p>Una settimana di studio oggi può risparmiarvi mesi di frustrazioni +domani. Sfruttare le risorse disponibili, quali tutorial, howto, +libri, documentazione, siti Web (come il Python cookbook) e soprattutto +i newsgroups. Ma prima di postare su di un newsgroup leggetevi "How to +ask smart questions" e ricordatevi sempre che "Google is you friend".</p> +</div> +<div class="section" id="disaccoppiamento-e-kiss"> +<h2><a class="toc-backref" href="#id3" name="disaccoppiamento-e-kiss"><em>Disaccoppiamento</em> e <em>KISS</em></a></h2> +<p>Se potere, scomponete l'applicazione in componenti separati e testateli +separatamente. Scegliete architetture che vi permettono di disaccoppiare +i problemi. Se potere fare le cose semplici, fatele semplici. +Abbiate un core minimale che faccia poco, ma che siate sicuri che +funzioni. Non complicatevi la vita.</p> +</div> +<div class="section" id="non-nascondete-gli-errori-sotto-il-tappeto"> +<h2><a class="toc-backref" href="#id4" name="non-nascondete-gli-errori-sotto-il-tappeto"><em>Non nascondete gli errori sotto il tappeto</em></a></h2> +<p>Non trappate l'inaspettato, è una ricetta sicura per avere rogne. +Usate lo <em>sviluppo incrementale</em>, cioè verificate il funzionamento del +programma SUBITO, e fissate l'errore SUBITO. Non debuggate mai +a posteriori, se potete evitarlo, ma scrivete codice testato fin da subito. +Abbiate una bella suite di test automatici di installazione, per accorgervi +da subito se c'è un problema quando installate il software su di un'altra +macchina.</p> +</div> +</div> +<div class="section" id="modulo-1-strumenti-di-introspezione-sviluppo-e-debugging"> +<h1><a class="toc-backref" href="#id5" name="modulo-1-strumenti-di-introspezione-sviluppo-e-debugging">Modulo 1: Strumenti di introspezione, sviluppo e debugging</a></h1> +<p><em>In questo modulo discuterò gli strumenti da me utilizzati per sviluppare +in Python sotto Windows. Parlerò di Cygwin come ambiente di lavoro, +di Python e di IPython come interpreti interattivi, di Idle e di PythonWin +come IDE, di pydoc e minidoc come tools di introspezione. Inoltre discuterò +alcune utili librerie e frameworks per Python (Numeric, matplotlib, gnuplot, +etc.).</em></p> +<div class="section" id="strumenti-di-introspezione-e-come-ottenere-aiuto"> +<h2><a class="toc-backref" href="#id6" name="strumenti-di-introspezione-e-come-ottenere-aiuto">Strumenti di introspezione e come ottenere aiuto</a></h2> +<ul class="simple"> +<li><strong>Help in linea:</strong> +Ottimo</li> +<li><strong>Pydoc:</strong> +Standard, può essere usato dalla riga di comando. +<tt class="docutils literal"><span class="pre">pydoc</span> <span class="pre">-g</span></tt> oppure <tt class="docutils literal"><span class="pre">python</span> <span class="pre">-mpydoc</span> <span class="pre">-g</span></tt> +vi dà la versione grafica, con funzionalità di search.</li> +<li><strong>Help di ActiveState:</strong> +Eccezionale, contiene anche libri di testo, FAQs e how-tos.</li> +<li><strong>Google:</strong> +Di tutto, di più.</li> +<li><strong>Newsgroups:</strong> +Risolvono i vostri problemi per voi, e gratis.</li> +</ul> +</div> +<div class="section" id="ambienti-di-sviluppo"> +<h2><a class="toc-backref" href="#id7" name="ambienti-di-sviluppo">Ambienti di sviluppo</a></h2> +<ul class="simple"> +<li><strong>Cygwin:</strong> +Emulatore Unix sotto Windows. Vi permette di lavorare dalla riga di +comando comodamente. Evitate di perdere tempo a cliccare a destra e +a manca e avete una stabilità maggiore di quella di un ambiente +grafico.</li> +<li><strong>Idle:</strong> +Ambiente di sviluppo per Python che viene con la distribuzione standard. +Un pò povero, con qualche difetto, ma semplice e portabile ovunque.</li> +<li><strong>PythonWin:</strong> +Ambiente di sviluppo per Python che viene con la distribuzione ActiveState +per Windows. Più sofisticato di Idle e più integrato con Windows. Ha dei +pro e dei contro.</li> +<li><strong>WingIDE, Eric/Qt Designer, Komodo, Boa Constructor:</strong> +Ambienti di sviluppo di cui esiste una versione commerciale. In generale +hanno un look più professionale e sono utili se uno deve dare interfacce +grafiche. WingIDE ha il debugger migliore, a quanto ho sentito.</li> +<li><strong>Emacs o Vi:</strong> +Un Real Programmer (TM) vi dirà che al mondo esistono due soli editor: +Emacs e Vi. Tutto il resto è spazzatura. Emacs e Vi girano bene sotto +Windows, ma funzionano al meglio sotto Unix.</li> +</ul> +</div> +<div class="section" id="strumenti-di-debugging"> +<h2><a class="toc-backref" href="#id8" name="strumenti-di-debugging">Strumenti di debugging</a></h2> +<ul class="simple"> +<li><strong>print:</strong> è la soluzione usata dai migliori programmatori Python;</li> +<li><strong>pdb:</strong> sembra il debugger dei poveri, ma è standard e funziona;</li> +<li><strong>mille altri debuggers</strong>, compreso un nuovissimo winpdb dall'autore +del pdb, da valutare;</li> +<li><strong>programmazione test-driven:</strong> con questa metodologia la stragrande +maggioranza dei bugs vengono individuati subito, non appena il +codice viene scritto, e vi troverete ad usare il debuggere dieci +volte di meno di quanto fate ora.</li> +</ul> +</div> +<div class="section" id="strumenti-utili-per-l-utenza-scientifica-ingegneristica"> +<h2><a class="toc-backref" href="#id9" name="strumenti-utili-per-l-utenza-scientifica-ingegneristica">Strumenti utili per l'utenza scientifica/ingegneristica</a></h2> +<ul class="simple"> +<li><strong>Numeric e/o Numarray:</strong> +Tutto quello che serve per far conti con le matrici. Numarray è il +successore di Numeric, con compatibilità al 99%.</li> +<li><strong>matplotlib:</strong> +Il meglio per plottare grafici 2D. Richiede Numeric/Numarray.</li> +<li><strong>ipython:</strong> +Il miglior interprete interattivo per Python. Eccezionali capacità +di introspezione e di debugging (è integrato con il Python debugger +pdb della libreria standard).</li> +</ul> +</div> +</div> +<div class="section" id="modulo-2-programmazione-di-base-in-python"> +<h1><a class="toc-backref" href="#id10" name="modulo-2-programmazione-di-base-in-python">Modulo 2: Programmazione di base in Python</a></h1> +<p><em>In questo modulo si ripasseranno molto brevemente le basi di Python e +si discuteranno le soluzioni al questionario di ammissione. Lo scopo +più che altro è quello di conoscersi e di chiarire il livello medio +dei partecipanti e coprire eventuali buchi nella preparazione di base.</em></p> +<p>Le soluzioni agli esercizi sono riportate nell'ultimo capitolo. Durante le +correzioni mi sono accordi di vari buchi nella programmazione +di base in Python che si è cercato di riempire.</p> +<div class="section" id="encoding"> +<h2><a class="toc-backref" href="#id11" name="encoding">Encoding</a></h2> +<p>[Illustrato al corso 1] In versioni recenti di Python (dalla 2.3) +se il vostro script contiene dei caratteri accentati (anche nei commenti) +ottenete un warning tipo questo:</p> +<pre class="literal-block"> +sys:1: DeprecationWarning: Non-ASCII character '\xe0' in file x.py +on line 1, but no encoding declared; see +http://www.python.org/peps/pep-0263.html for details +</pre> +<p>La soluzione e' aggiungere in testa al vostro programma una dichiarazione +tipo questa:</p> +<pre class="literal-block"> +#-*- encoding: latin-1 -*- +</pre> +<p>(usate latin-15 se il vostro programma contiene il simbolo dell'Euro).</p> +</div> +<div class="section" id="pathnames"> +<h2><a class="toc-backref" href="#id12" name="pathnames">Pathnames</a></h2> +<p>Sfortunatamente Windows usa la backslash come separatore dei pathnames; +la backslash e' anche il carattere di escaping, quindi ci sono +problemi:</p> +<pre class="doctest-block"> +>>> print 'documenti\nonna' # \n interpretato come newline +documenti +onna +</pre> +<p>La soluzione e' usare raw strings:</p> +<pre class="doctest-block"> +>>> print r'documenti\nonna' +documenti\nonna +</pre> +<p>Alternativamente, in versioni recenti di Windows, avreste potuto usare +anche "/" come separatore:</p> +<pre class="doctest-block"> +>>> import os +>>> os.path.exists(r'C:\Python24\python.exe') +True +>>> os.path.exists(r'C:/Python24/python.exe') +True +</pre> +</div> +<div class="section" id="insiemi"> +<h2><a class="toc-backref" href="#id13" name="insiemi">Insiemi</a></h2> +<p>In Python 2.3 gli insiemi sono stati aggiunti come un nuovo tipo di dati +nel modulo sets (by Alex Martelli); in Python 2.4 gli insiemi sono diventati +un tipo builtin.</p> +<pre class="doctest-block"> +>>> s = set('pippo') +>>> s +set(['i', 'p', 'o']) +>>> 'i' in s +True +>>> 'p' in s +True +>>> 'o' in s +True +>>> 'z' in s +False +</pre> +<p>E' possibile calcolare l'unione e l'intersezione di insiemi:</p> +<pre class="doctest-block"> +>>> t = set('aglio') +>>> s | t +set(['a', 'g', 'i', 'l', 'o', 'p']) +>>> s & t +set(['i', 'o']) +</pre> +<p>Il modo compatibile con il passato per usare i sets nello stesso modo nella +2.3 e nella 2.4 è il seguente:</p> +<pre class="literal-block"> +try: + set: +except NameError: + from sets import Set as set +</pre> +<p>Guardare la documentazione standard per saperne di più.</p> +</div> +<div class="section" id="differenza-tra-mutabili-e-immutabili"> +<h2><a class="toc-backref" href="#id14" name="differenza-tra-mutabili-e-immutabili">Differenza tra mutabili e immutabili</a></h2> +<p>Questa è una causa comune di confusione in Python:</p> +<pre class="doctest-block"> +>>> a = 1 # i numeri sono immutabili +>>> b = a +>>> a += 1 +>>> a +2 +>>> b +1 +</pre> +<pre class="doctest-block"> +>>> a = [1] # le liste sono mutabili +>>> b = a +>>> a += [1] +>>> a +[1, 1] +>>> b +[1, 1] +</pre> +</div> +<div class="section" id="getattr-e-setattr"> +<h2><a class="toc-backref" href="#id15" name="getattr-e-setattr">getattr e setattr</a></h2> +<p>[Svolto al corso 1] <tt class="docutils literal"><span class="pre">getattr</span></tt> e <tt class="docutils literal"><span class="pre">setattr</span></tt> servono per richiamare e +settare metodi il cui nome viene determinato dinamicamente:</p> +<pre class="literal-block"> +#<getattr_ex.py> + +class C(object): + def m1(self): + print "chiamato m1" + def m2(self): + print "chiamato m2" + +if __name__ == "__main__": + c = C() + method = raw_input("Che metodo devo chiamare? [m1 o m2] ") + getattr(c, method)() + +#</getattr_ex.py> +</pre> +<p><em>Non usate exec quando getattr basterebbe!</em></p> +<pre class="doctest-block"> +>>> method = 'm1' +>>> exec 'c.%s()' % method # funziona ma è brutto +chiamato m1 +>>> getattr(c, method)() # il modo giusto +chiamato m1 +</pre> +<p>Per <tt class="docutils literal"><span class="pre">setattr</span></tt> vedere la documentazione.</p> +</div> +<div class="section" id="gestione-dei-processi"> +<h2><a class="toc-backref" href="#id16" name="gestione-dei-processi">Gestione dei processi</a></h2> +<p>Come far partire un processo in parallelo:</p> +<pre class="literal-block"> +import subprocess + +PLAYER ="mplay32" + +def play_song(song): + subprocess.Popen([PLAYER, "/play", "/close", song]) # NON BLOCCA! + print "Partito" + + +if __name__ == "__main__": + play_song("c:/Documents and Settings/micheles/Desktop/Music/1. " + "Theme from Harry's Game.mp3") +</pre> +<p><tt class="docutils literal"><span class="pre">subprocess.call</span></tt> fa partire il processo e blocca il programma fintanto +che il processo non è terminato. Ho anche fatto vedere cosa succede +se uno dei processi solleva qualche eccezione inaspettata ma viene chiuso +correttamente grazie al try .. finally:</p> +<pre class="literal-block"> +#<main.py> + +"Chiama due processi proc1a.py e proc1b.py" + +import subprocess + +CMD_a = ["python", "-c", "import proc1a; proc1a.main()"] +CMD_b = ["python", "-c", "import proc1b; proc1b.main()"] + +if __name__ == "__main__": + p_a = subprocess.Popen(CMD_a) + p_b = subprocess.Popen(CMD_b) + +#</main.py> +</pre> +<p>Processo 1a:</p> +<pre class="literal-block"> +#<proc1a.py> + +import time + +def main(): + for i in range(10): + print "hello" + time.sleep(1) + +if __name__ == "__main__": + main() + +#</proc1a.py> +</pre> +<p>Processo 1b:</p> +<pre class="literal-block"> +#<proc1b.py> + +#-*- encoding: latin-1 -*- +import time, sys + +def main(): + try: + f = file("proc1b.py") + for i in range(10): + print "world" + if i == 5: + raise RuntimeError("Ahia!") + time.sleep(1) + finally: + f.close() + print "Il file è stato chiuso correttamente." + +if __name__ == "__main__": + main() + +#</proc1b.py> +</pre> +<p>Ho anche illustrato brevemente come si possono gestire i processi da +Twisted:</p> +<pre class="literal-block"> +#<proc2a.py> + +"Un processo che genera numeri casuali e li salva nel file data.txt" + +import random + +def main(): + ro = random.Random() + out = file("data.txt", "w") + for number in ro.sample(range(1000), 100): + print >> out, number + out.close() + print "Dati salvati sul file 'data.txt'" + +if __name__ == "__main__": + main() + +#</proc2a.py> + +#<proc2b.py> + +"Un processo che genera l'istogramma histo.png dai dati in data.txt" + +from pylab import hist, savefig + +def main(): + hist([int(n) for n in file("dat.txt")], 10) + savefig("histo.png") + print "Istogramma salvato sul file 'histo.png'" + +if __name__ == "__main__": + main() + +#</proc2b.py> + +#<twisted_main.py> + +"Il main che chiama proc2a.py e proc2b.py nell'ordine e gestisce gli errori" + +import webbrowser, sys +if sys.platform == "win32": + from twisted.internet import win32eventreactor + win32eventreactor.install() + +from twisted.internet.utils import getProcessOutput +from twisted.internet import reactor + +def scrivi_messaggio(err): + print err.getErrorMessage() + reactor.stop() + import pdb; pdb.set_trace() # fa partire il debugger in caso di errore + +def visualizza_histo(out_di_genera_histo): + print out_di_genera_histo + webbrowser.open("histo.png") + +def genera_histo(out_di_genera_dati): + print out_di_genera_dati + getProcessOutput(sys.executable, (r"c:\corso\processi\proc2b.py",)) \ + .addCallback(visualizza_histo) \ + .addErrback(scrivi_messaggio) + +def genera_dati(): + getProcessOutput(sys.executable, (r"c:\corso\processi\proc2a.py",)) \ + .addCallback(genera_histo) \ + .addErrback(scrivi_messaggio) + +if __name__ == "__main__": + reactor.callLater(0, genera_dati) # call "genera_dati" after 0 seconds + reactor.run() + +#</twisted_main.py> +</pre> +<p>In questo esempio ho usato <tt class="docutils literal"><span class="pre">sys.executable</span></tt>, che contiene il nome +completo dell'eseguibile Python (per esempio <tt class="docutils literal"><span class="pre">C:\Python24\python.exe</span></tt>) +con cui il programma principale è stato lanciato. Questo assicura che +i processi secondari vengano lanciati con quella versione di Python +(utile se avete installato contemporaneamente piu' versioni di Python +e ci possono essere dei dubbi, oppure se il path non e' settato +correttamente e l'eseguibile Python non viene trovato).</p> +<p>A volte, nonostante tutta la buona volontà, i processi vanno fuori +controllo. E' possibile ammazzarli brutalmente, con una funzione +<tt class="docutils literal"><span class="pre">kill</span></tt> come la seguente:</p> +<pre class="literal-block"> +import os + +try: # we are on Windows + import win32api + def kill(pid, *unix_compatibility_dummy_args): + handle = win32api.OpenProcess(1, False, pid) # open handle to kill + win32api.TerminateProcess(handle, -1) + win32api.CloseHandle(handle) + os.kill = kill # fix os +except ImportError: # we are on Unix + pass +</pre> +<p>In questo modo di fare, il modulo 'os' della libreria standard viene +fissato automaticamente, aggiungendogli una funzione 'kill' che è +mancante nell'ambiente Windows ma che può facilmente essere implementata +usando le win32api (che non vengono con la distribuzione standard ma sono +incluse con la distribuzione dell'ActiveState).</p> +<p>Naturalmente cambiare moduli della libreria standard al volo NON È +CONSIGLIATO, ma è sempre meglio che modificare a mano il codice +sorgente e mantenere una propria versione modificata.</p> +</div> +<div class="section" id="iteratori-e-generatori"> +<h2><a class="toc-backref" href="#id17" name="iteratori-e-generatori">Iteratori e generatori</a></h2> +<p>Un iterabile è un qualunque oggetto su cui si può iterare con un +ciclo "for"; un iteratore è un oggetto con un metodo .next(). +Il modo più comune per definire iteratori è tramite un generatore, +cioè una "funzione" con uno "yield":</p> +<pre class="doctest-block"> +>>> def gen123(): +... yield 1 +... yield 2 +... yield 3 +... +>>> it = gen123() +>>> it.next() +1 +>>> it.next() +2 +>>> it.next() +3 +>>> it.next() +Traceback (most recent call last): + File '<stdin>', line 1, in ? +StopIteration +</pre> +<p>Un ciclo "for" internamente converte l'iterabile in un iteratore, +chiama il metodo ".next()" successivamente e trappa l'eccezione StopIteration, +uscendo dal loop quando non c'è più nulla su cui iterare:</p> +<pre class="doctest-block"> +>>> it = gen123() +>>> for i in it: print i +... +1 +2 +3 +</pre> +</div> +</div> +<div class="section" id="modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni"> +<h1><a class="toc-backref" href="#id18" name="modulo-3-tipici-errori-di-programmazione-e-gestione-delle-eccezioni">Modulo 3. Tipici errori di programmazione e gestione delle eccezioni</a></h1> +<p><em>Si discuteranno buoni e cattivi esempi di programmazione presi da software +reale scritto alla Magneti Marelli. Si discuteranno alcune tecniche +per interpretare i tracebacks di Python e per identificare l'origine dei +problemi.</em></p> +<div class="section" id="l-errore-pi-tipico-con-le-eccezioni"> +<h2><a class="toc-backref" href="#id19" name="l-errore-pi-tipico-con-le-eccezioni">L'errore più tipico con le eccezioni</a></h2> +<p>Bisogna assolutamente evitare codice come il seguente:</p> +<pre class="literal-block"> +try: + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla + bla-bla +except: + bla-bla +</pre> +<p>Nel blocco <tt class="docutils literal"><span class="pre">try</span></tt> dev'esserci la cosa più semplice possibile, per +limitare i tipi di eccezione che possono nascere. Inoltre l'except nudo +e' orribile perchè trappa qualunque cosa, anche quello che non vorreste. +La cosa giusta da fare è del tipo:</p> +<pre class="literal-block"> +try: + bla-bla +except MyException, e: # sempre specificare l'eccezione aspettata + print e +except OtherException,e: + print e +... +</pre> +<p>Non trappate l'inaspettato, altrimenti non capirete mai qual è stata +l'origine di un problema.</p> +</div> +<div class="section" id="il-try-finally-una-grande-idea"> +<h2><a class="toc-backref" href="#id20" name="il-try-finally-una-grande-idea">Il try .. finally è una grande idea</a></h2> +<p>Molto spesso quello che volete non è tanto il try .. except, quanto il +try .. finally: il vantaggio del try .. finally è che +<em>non vi nasconde l'eccezione</em> e nello stesso tempo vi <em>garantisce che quello +che deve essere chiuso correttamente venga chiuso correttamente</em> in ogni caso. +C'è un eccezione a questa regola: se ammazzate un processo di brutto con un +kill, il try .. finally non può salvarvi. Il try .. finally vi +salva per tutte le eccezioni Python, compreso il CTRL-C (KeyboardInterrupt) +e il sys.exit() (SystemExit).</p> +<pre class="doctest-block"> +>>> try: +... raise RuntimeError("Ahia") +... finally: +... print "Io vengo eseguito SEMPRE, anche se c'è un'eccezione!" +... +Io vengo eseguito SEMPRE, anche se c'e' un'eccezione! +Traceback (most recent call last): + File '<stdin>', line 2, in ? +RuntimeError: Ahia +</pre> +</div> +<div class="section" id="uso-di-assert"> +<h2><a class="toc-backref" href="#id21" name="uso-di-assert">Uso di assert</a></h2> +<p>Per asserire che una condizione è verificata con certezza:</p> +<pre class="doctest-block"> +>>> def div2(x): +... assert isinstance(x, int), '%s non è un numero intero' % x +... return x/2 +... +>>> div2(14) +7 +>>> div2(14.0) +Traceback (most recent call last): + File '<stdin>', line 1, in ? + File '<stdin>', line 2, in div2 +AssertionError: 14.0 non è un numero intero +</pre> +<p>Tipicamente si usa in "sanity checks", per essere sicuri che un parametro +sia esattamente quello che ci si aspetta, in casi di eccezioni gravi; se +l'assert non è rispettato, tutto il programma deve bloccarsi.</p> +</div> +<div class="section" id="non-usate-exec"> +<h2><a class="toc-backref" href="#id22" name="non-usate-exec">Non usate exec</a></h2> +<p>'exec' è un costrutto pericolosissimo che va riservato solo a chi sa +cosa sta facendo. Spesso e volentieri si usa soltanto per ignoranza +dell'esistenza di una soluzione migliore.</p> +<p>Esempio:</p> +<pre class="literal-block"> +exec file("myscript.py").read() +</pre> +<p>è UN ERRORE GRAVE per vari motivi.</p> +<p>In primo luogo, in caso di eccezioni sollevate in 'myscript.py' +perdete informazione su dove si trova l'errore (cioè nel file "myscript.py") +e rendete impossibile la vita al debugger; in secondo luogo, sporcate il +namespace del vostro programma in maniera potenzialmente pericolosa. La cosa +giusta da fare è:</p> +<pre class="literal-block"> +dic = {} +execfile("myscript.py", dic) +</pre> +<p>che non perde informazioni e non sporca il namespace, perchè i nomi +definiti in myscript.py verranno confinati nel dizionario. Leggetevi +la documentazione di 'execfilè per saperne di più.</p> +</div> +<div class="section" id="come-far-partire-pdb-automaticamente-in-caso-di-eccezioni-inaspettate"> +<h2><a class="toc-backref" href="#id23" name="come-far-partire-pdb-automaticamente-in-caso-di-eccezioni-inaspettate">Come far partire pdb automaticamente in caso di eccezioni inaspettate</a></h2> +<p>[Illustrato al corso 1]</p> +<pre class="literal-block"> +#<exc_debug.py> + +# recipe in the Python cookbook first edition, chapter 14.5 + +import sys + +def info(type, value, tb): + if hasattr(sys, 'ps1') or not sys.stderr.isatty() or \ + type == SyntaxError: + # we are in interactive mode or we don't have a tty-like + # device, so we call the default hook + sys.__excepthook__(type, value, tb) + else: + import traceback, pdb + # we are NOT in interactive mode, print the exception... + traceback.print_exception(type, value, tb) + print + # ...then start the debugger in post-mortem mode. + pdb.pm() + +sys.excepthook = info + +#</exc_debug.py> +</pre> +<p>Se un programma importa <tt class="docutils literal"><span class="pre">exc_debug</span></tt>, il Python debugger partirà +automaticamente in caso di eccezioni non trappate. Per esempio eseguire</p> +<pre class="literal-block"> +#<example.py> +import exc_debug +a = 1 +b = 0 +a/b +#</example.py> +</pre> +<p>fa partire il debugger:</p> +<pre class="literal-block"> +$ python example.py +Traceback (most recent call last): + File "example.py", line 4, in ? + a/b +ZeroDivisionError: integer division or modulo by zero + +> /mnt/hda2/cygwin/home/micheles/md/pypers/marelli/materiale/example.py(4)?() +-> a/b +(Pdb) print a +1 +(Pdb) print b +0 +Pdb) !b = 1 # cambia il valore di b +(Pdb) print a/b +1 +</pre> +<p>Date <tt class="docutils literal"><span class="pre">help</span> <span class="pre">pdb</span></tt> dall'interno del debugger per avere informazioni sul suo +funzionamento.</p> +</div> +<div class="section" id="eccezioni-e-threads"> +<h2><a class="toc-backref" href="#id24" name="eccezioni-e-threads">Eccezioni e threads</a></h2> +<p>Un'eccezione non trappata blocca soltanto il thread in cui si è verificata, +NON tutto il programma:</p> +<pre class="literal-block"> +#<esempio1.py> + +import threading, time, sys + +def print_hello(): + for i in range(10): + print "hello" + if i == 5: + raise RuntimeError("Problema a runtime") + time.sleep(1) + +def print_world(): + for i in range(10): + print "world" + time.sleep(1) + +threading.Thread(target=print_hello).start() +threading.Thread(target=print_world).start() + +#</esempio1.py> +</pre> +<p>dà come output:</p> +<pre class="literal-block"> +$ python esempio1.py +hello +world +hello +world +hello +world +hello +world +hello +world +hello +world +Exception in thread Thread-1: +Traceback (most recent call last): + File "/usr/lib/python2.4/threading.py", line 442, in __bootstrap + self.run() + File "/usr/lib/python2.4/threading.py", line 422, in run + self.__target(*self.__args, **self.__kwargs) + File "esempio1.py", line 7, in print_hello + raise RuntimeError("Problema a runtime") +RuntimeError: Problema a runtime + +world +world +world +world +</pre> +<p>Quindi uno è costretto a implementare un meccanismo di controllo, tipo +il seguente:</p> +<pre class="literal-block"> +import threading, time, sys + +END = False + +def print_hello(): + global END + i = 0 + while not END: + i += 1 + print "hello" + if i == 5: + try: + raise RuntimeError("Problema a runtime") + except RuntimeError, e: + END = True + time.sleep(1) + +def print_world(): + i = 0 + while not END: + print "world" + time.sleep(1) + +threading.Thread(target=print_hello).start() +threading.Thread(target=print_world).start() +</pre> +<p>Questa è una soluzione artigianale, che usa una variabile globale, sfruttando +il fatto che le variabili globali sono condivise fra tutti i thread (questo +può anche causare danni, se non si sta attenti).</p> +<p>La strada consigliata per comunicare fra threads è quella di usare una coda +[esempio svolto al corso 1]:</p> +<pre class="literal-block"> +"Esempio di due threads comunicanti tramite una queue." + +import time, threading, Tkinter, Queue + +queue = Queue.Queue() + +def print_hello(): + for i in range(10): + try: + messaggio = queue.get_nowait() + except Queue.Empty: + pass + else: + if messaggio == "terminate": break + print "%s, hello" % i + time.sleep(1) + +def print_world(): + for i in range(10): + print "%s, world" % i + if i == 5: + queue.put("terminate") # manda il messaggio di terminazione + root.quit() + raise RuntimeError("Errore nel thread print_world!") + time.sleep(1) + +root = Tkinter.Tk() + +for func in print_hello, print_world: + th = threading.Thread(target=func) + th.start() + +root.mainloop() +</pre> +<p>Questo esempio fa anche vedere che chiudere la finestrella grafica NON +uccide i threads che stanno girando.</p> +<p>I meccanismi di controllo per bloccare i thread sono notoriamente fragili +e piccoli errori possono farvi grossi danni.</p> +<p>Debuggare i threads è notoriamente un macello.</p> +<p>L'unica soluzione vera è evitare i threads quando è possibile.</p> +</div> +</div> +<div class="section" id="modulo-4-sviluppo-orientato-ai-test"> +<h1><a class="toc-backref" href="#id25" name="modulo-4-sviluppo-orientato-ai-test">Modulo 4. Sviluppo orientato ai test</a></h1> +<p><em>Come scrivere software con tecnologie agili, con lo scopo di ridurre e +tenere sotto controllo i bugs. Discussione di doctest, py.test e unittest. +Esempi di programmazione test driven.</em></p> +<div class="section" id="consigli-di-carattere-generale"> +<h2><a class="toc-backref" href="#id26" name="consigli-di-carattere-generale">Consigli di carattere generale</a></h2> +<ul class="simple"> +<li>testate SUBITO, non debuggate dopo (ad ogni una riga di codice che si +scrive, si testa che funzioni e che non distrugga quello che funzionava +prima).</li> +<li>scrivete software in maniera che sia testabile (per esempio create +sempre anche una versione non-grafica di un programma grafico, perchè +la versione testuale si testa <strong>molto</strong> più facilmente).</li> +<li>non abbiate paura a scrivervi un vostro ambiente di test personalizzato.</li> +<li>tenete conto che unittest e doctest esistono e possono aiutarvi infinitamente +nel gestire i vostri test.</li> +</ul> +</div> +<div class="section" id="usare-unittest"> +<h2><a class="toc-backref" href="#id27" name="usare-unittest">Usare unittest</a></h2> +<p>Tipicamente con unittest si divide la libreria da testare:</p> +<pre class="literal-block"> +#<isnumber.py> + +def is_number(arg): + "Verifica se la stringa arg è un numero valido" + try: + float(arg) + except ValueError: + return False + else: + return True + +#</isnumber.py> +</pre> +<p>dal file di test, che convenzionalmente ha un nome che inizia per "test":</p> +<pre class="literal-block"> +#<test_isnumber.py> + +import unittest + +from isnumber import is_number + +class TestIsNumber(unittest.TestCase): + + def setUp(self): + print "sto inizializzando" + + # test positivi + def test_1(self): + "Testa che '1' è un numero buono." + self.assertTrue(is_number("1")) + def test_2(self): + "Testa che '1.3' è un numero buono." + self.assertTrue(is_number("1.3")) + def test_3(self): + "Testa che '+1.3' è un numero buono." + self.assertTrue(is_number("+1.3")) + def test_4(self): + "Testa che '-1.3' è un numero buono." + self.assertTrue(is_number("-1.3")) + + # test negativi + def test_5(self): + "Testa che '1-.3' non è un numero buono." + self.assertFalse(is_number("1-.3")) + def test_6(self): + "Testa che 'à non è un numero buono." + self.assertFalse(is_number("a")) + def test_7(self): + "Testa che '42' è un numero buono." + self.assertTrue(is_number("42")) + + def tearDown(self): + print "Sto chiudendo quello che c'è da chiudere" + +if __name__ == "__main__": + unittest.main() + +#</test_isnumber.py> +</pre> +<p>Eseguire i tests con l'opzione verbose da:</p> +<pre class="literal-block"> +$ python test_isnumber.py -v +Testa che '1' è un numero buono. ... sto inizializzando +Sto chiudendo quello che c'è da chiudere +ok +Testa che '1.3' è un numero buono. ... sto inizializzando +Sto chiudendo quello che c'è da chiudere +ok +Testa che '+1.3' è un numero buono. ... sto inizializzando +Sto chiudendo quello che c'è da chiudere +ok +Testa che '-1.3' è un numero buono. ... sto inizializzando +Sto chiudendo quello che c'è da chiudere +ok +Testa che '1-.3' non è un numero buono. ... sto inizializzando +Sto chiudendo quello che c'è da chiudere +ok +Testa che 'à non è un numero buono. ... sto inizializzando +Sto chiudendo quello che c'è da chiudere +ok +Testa che '42' è un numero buono. ... sto inizializzando +Sto chiudendo quello che c'è da chiudere +ok + +---------------------------------------------------------------------- +Ran 7 tests in 0.001s + +OK +</pre> +<p>Questo mostra che i metodi <tt class="docutils literal"><span class="pre">setUp</span></tt> e <tt class="docutils literal"><span class="pre">tearDown</span></tt> vengono chiamati +<em>per ogni test</em>, quindi tutti i test si svolgono in un ambiente pulito.</p> +<p>E' normale avere un file di test più lungo della libreria da testare.</p> +<p>E' possibile specificare le eccezioni aspettate:</p> +<pre class="literal-block"> +#<test_exc.py> + +import unittest + +def divide(a, b): + return a/b + +class TestIsNumber(unittest.TestCase): + def test_1(self): + "Divide 4/2" + self.assertEqual(divide(4,2), 2) + def test_2(self): + "Divide 4/0" + self.assertRaises(ZeroDivisionError, divide, 4, 0) + + +if __name__ == "__main__": + unittest.main() + +#</test_exc.py> + +$ python test_exc.py -v +Divide 4/2 ... ok +Divide 4/0 ... ok + +---------------------------------------------------------------------- +Ran 2 tests in 0.001s + +OK +</pre> +</div> +<div class="section" id="usare-doctest"> +<h2><a class="toc-backref" href="#id28" name="usare-doctest">Usare doctest</a></h2> +<p>L'uso più semplice, eseguire i doctest che si trovano nelle stringhe +di documentazione di un modulo:</p> +<pre class="literal-block"> +#<esempio_banale.py> + +def sum12(): + """Questa funzione ritorna la somma di 1 + 2:: + >>> sum12() + 3""" + return 1+2 + +if __name__ == "__main__": + import doctest; doctest.testmod() + +#</esempio_banale.py> +</pre> +<p>Ecco come eseguire i tests con l'opzione verbose:</p> +<pre class="literal-block"> +$ python esempio_banale.py -v +Trying: + sum12() +Expecting: + 3 +ok +1 items had no tests: + __main__ +1 items passed all tests: + 1 tests in __main__.sum12 +1 tests in 2 items. +1 passed and 0 failed. +Test passed. +</pre> +<p>Eseguire i doctest che si trovano in un file di testo separato (nuova +funzionalità di Python 2.4):</p> +<pre class="literal-block"> +#<doctest_runner.py> + +import doctest + +if __name__== "__main__": + doctest.testfile("test_isnumber.txt") + +#</doctest_runner.py> +</pre> +<p>Contenuto di 'test_isnumber.txt':</p> +<pre class="literal-block"> +Questa è la documentazione della funzione isnumber +==================================================== + +Esempi di uso: + +>>> from isnumber import is_number +>>> is_number("1") +True +>>> is_number("1.3") +True +>>> is_number("+1.3") +True +>>> is_number("-1.3") +True +>>> is_number("1-.3") +False +>>> is_number("a") +False +>>> is_number("42") +True +>>> 1/0 +Traceback (most recent call last): + kkkkdjjfkf +ZeroDivisionError: integer division or modulo by zero +</pre> +<p>Eseguire i tests:</p> +<pre class="literal-block"> +$ python doctest_runner.py -v +Trying: + from isnumber import is_number +Expecting nothing +ok +Trying: + is_number("1") +Expecting: + True +ok +Trying: + is_number("1.3") +Expecting: + True +ok +Trying: + is_number("+1.3") +Expecting: + True +ok +Trying: + is_number("-1.3") +Expecting: + True +ok +Trying: + is_number("1-.3") +Expecting: + False +ok +Trying: + is_number("a") +Expecting: + False +ok +Trying: + is_number("42") +Expecting: + True +ok +Trying: + 1/0 +Expecting: + Traceback (most recent call last): + kkkkdjjfkf + ZeroDivisionError: integer division or modulo by zero +ok +1 items passed all tests: + 9 tests in test_isnumber.txt +9 tests in 1 items. +9 passed and 0 failed. +Test passed. +</pre> +<p>Confrontare la leggibilità di <tt class="docutils literal"><span class="pre">test_isnumber.txt</span></tt> con la leggibilità +di <tt class="docutils literal"><span class="pre">test_isnumber.py</span></tt>, basato su unittest. Leggetevi il mio seminario su +doctest (in allegato) per convincervi che doctest è il migliore. Anche perchè +i doctest possono essere convertiti in unittest automaticamente, a partire +da Python 2.4</p> +<p>Per convertire i test contenuti in 'mymodulè da doctest a unittest:</p> +<pre class="literal-block"> +import doctest, unittest, mymodule + +if __name__== "__main__": + suite = doctest.DocTestSuite(mymodule) + unittest.TextTestRunner(verbosity=2).run(suite) +</pre> +<p>E' anche possibile contenere i tests contenuti in un file di +tipo testo da doctest a unittest, vedere la documentazione.</p> +<p>Correntemente, doctest non ha un meccanismo predefinito corrispondente +ai metodi <tt class="docutils literal"><span class="pre">setUp</span></tt> e <tt class="docutils literal"><span class="pre">tearDown</span></tt> di unittest, ma potete impostarlo a mano.</p> +</div> +<div class="section" id="un-esempio-di-programma-sviluppato-in-maniera-incrementale"> +<h2><a class="toc-backref" href="#id29" name="un-esempio-di-programma-sviluppato-in-maniera-incrementale">Un esempio di programma sviluppato in maniera incrementale</a></h2> +<p>Il seguente esempio svolto al corso intendeva dimostrare:</p> +<ol class="arabic simple"> +<li>Come si sviluppa un programma in maniera incrementale (ad ogni nuova riga di +codice si fa una verifica immediata del funzionamento dell'insieme);</li> +<li>Come si fanno scelte architetturali in maniera tale da assicurare la +testabilità del prodotto finale in maniera semplice ed automatica;</li> +<li>Come sfruttare la libreria standard di Python al meglio, usando il +modulo cmd;</li> +<li>Come dividere il codice in metodi pubblici, metodi di utilità e +metodi di debugging;</li> +<li>Come assicurarsi che il programma venga chiuso propriamente, anche +nel caso di eccezioni impreviste e imprevedibili;</li> +</ol> +<pre class="literal-block"> +# -*- encoding: latin-1 -*- +import os, cmd, subprocess, lib, time # lib fissa os.kill +from pywintypes import error as WindowsProcessDidNotStartCorrectly + +MUSICDIR = "C:/Documents and Settings/micheles/Desktop/Music" + +def play_song(name): + po = subprocess.Popen(["mplay32", "/play", "/close", name]) + return po.pid + +class InterpreteDiComandi(cmd.Cmd): + cwd = MUSICDIR + prompt = "Player> " + def preloop(self): + self.process_list = [] + os.chdir(MUSICDIR) + + def do_play(self, arg): + "Suona una canzone" + if not arg: + print "Per favore scrivi il nome di una canzone!" + else: + self.process_list.append(play_song(arg)) + + def do_quit(self, dummy): + "Esce dall'interprete." + return True + + def safe_kill(self, pid): + try: + os.kill(pid) + except WindowsProcessDidNotStartCorrectly, e: + print e + + def do_kill(self, arg): + "Uccide il player" + try: + pid = self.process_list.pop() + except IndexError: + print "Hai già ucciso tutti i processi!" + else: + self.safe_kill(pid) + + def postloop(self): + for pid in self.process_list: + self.safe_kill(pid) + print "Ho ucciso tutto!" + os.chdir(os.path.dirname(os.path.abspath(__file__))) + + # metodi che possono essere utili per il debugging + def do_print_dir(self, arg): + "Comando utile per il debugging" + print self.cwd + + def do_raise_exc(self, dummy): + raise RuntimeError("Tanto per vedere che succede") + + def do_sleep(self, arg): + "utile per vedere quello che i test automatici stanno facendo" + time.sleep(int(arg)) + +i = InterpreteDiComandi() + +try: + i.cmdloop() +finally: # assicura la chiusura anche in caso di eccezione + i.postloop() +</pre> +<p>Nel caso regolare (senza eccezioni impreviste) .postloop è chiamato 2 volte, +ma questo non fa danno.</p> +<p>Questo potrebbe essere il codice corrispondente ad un test automatico:</p> +<pre class="literal-block"> +$ more test_player.cmd +play +sleep 2 +play 2. Croi Croga.mp +sleep 2 +play 2. Croi Croga.mp3 +sleep 2 +kill +sleep 2 +quit +</pre> +<p>(la prima canzone non esiste, in modo da poter vedere come viene segnalato +l'errore) che verrebbe eseguito così:</p> +<pre class="literal-block"> +$ python player.py < test_player.cmd +</pre> +</div> +</div> +<div class="section" id="modulo-5-design-documentazione-e-manutenzione-di-librarie"> +<h1><a class="toc-backref" href="#id30" name="modulo-5-design-documentazione-e-manutenzione-di-librarie">Modulo 5: Design, documentazione e manutenzione di librarie</a></h1> +<p><em>Pratiche di programmazione "in the large". Moduli, packages, strumenti di +documentazione e di installazione. Applicazioni pratiche di principi generali +quali disaccoppiamento, modularità, non duplicazione del codice.</em></p> +<div class="section" id="la-filosofia-del-python"> +<h2><a class="toc-backref" href="#id31" name="la-filosofia-del-python">La filosofia del Python</a></h2> +<pre class="doctest-block"> +>>> import this +The Zen of Python, by Tim Peters +Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +Readability counts. +Special cases aren't special enough to break the rules. +Although practicality beats purity. +Errors should never pass silently. +Unless explicitly silenced. +In the face of ambiguity, refuse the temptation to guess. +There should be one-- and preferably only one --obvious way to do it. +Although that way may not be obvious at first unless you are Dutch. +Now is better than never. +Although never is often better than *right* now. +If the implementation is hard to explain, it's a bad idea. +If the implementation is easy to explain, it may be a good idea. +Namespaces are one honking great idea -- let's do more of those! +</pre> +</div> +<div class="section" id="principio-del-disaccoppiamento"> +<h2><a class="toc-backref" href="#id32" name="principio-del-disaccoppiamento">Principio del disaccoppiamento</a></h2> +<p>Questo è il principio guida di tutta la programmazione e non solo, tutto +il resto nasce di conseguenza.</p> +<p>Il principio dice: <strong>cercate di disaccoppiare il più possibile +le componenti del vostro sistema</strong>.</p> +<p><em>Un componente si dice disaccoppiato se +può essere rimosso o sostituito senza danneggiare il resto del sistema.</em></p> +<p>Per esempio:</p> +<ul class="simple"> +<li>disaccoppiare l'ambiente di sviluppo dall'ambiente di esecuzione (è più +sicuro lanciare un programma dalla riga di comando che dall' IDE; un +debugger disaccoppiato come pdb è più sicuro di un debugger integrato +come quello di PythoWin, un editor vero è più sicuro dell'editor dell' +IDE, etc.)</li> +<li>disaccoppiare l'interfaccia grafica: il programma deve poter funzionare +senza di essa, o sostituendo il GUI toolkit con un altro.</li> +<li>i threads vi accoppiano tutto, se uno va male può bloccare tutti gli +altri: evitateli se potete.</li> +<li>l'ereditarietà vi accoppia il parente al figlio (per sapere quello che +fa il figlio bisogna andare a vedere quello che fanno il padre, il nonno, +il bisnonno, il trisavolo, etc.). Evitatela se potete.</li> +<li>la modularità è un modo concreto per applicare il principio del +disaccoppiamento al codice: funzioni che logicamente vanno insieme, +vanno messe in un modulo comune, funzioni separate vanno separate. +Se c'è un problema nel modulo X, questo non deve interferire con +il modulo Y (almeno in un mondo ideale). La modularità rende anche +più semplice rimpiazzare un modulo sbagliato o vecchio con uno +corretto o più recente.</li> +<li>la non-duplicazione del codice è una conseguenza della modularità: +avendo raggruppato le funzioni di uso generale in un modulo comune, +si acquista in concisione, semplicità, leggibilità, debuggabilità, +si evita di correggere lo stesso errore più volte, e in generale tutto +diviene più facile da mantenere.</li> +</ul> +</div> +<div class="section" id="principio-del-kiss"> +<h2><a class="toc-backref" href="#id33" name="principio-del-kiss">Principio del KISS</a></h2> +<p><em>Keep it simple, stupid</em>. In Italiano: <em>non facciamoci del male</em>.</p> +<p>Se potete fare le cose in maniera semplice, fatele in maniera semplice.</p> +<p>Chiedetevi sempre se una cosa vi serve veramente oppure no.</p> +</div> +<div class="section" id="importanza-di-avere-un-prototipo"> +<h2><a class="toc-backref" href="#id34" name="importanza-di-avere-un-prototipo">Importanza di avere un prototipo</a></h2> +<p>Non c'è nulla di più sbagliato che partire scrivendosi l'architettura di +un progetto complesso sulla carta. Si parte sempre scrivendo un prototipo, +<em>testato sul campo di battaglia</em>, che magari ha l'1% delle funzionalità +che vi interessano, ma che <em>funziona</em>. A quel punto è possibile decidere +l'architettura e il prototipo diventerà il core e la parte più testata +e sicura della vostra applicazione.</p> +<p>Il prototipo/core deve <em>rimuovere tutto l'inessenziale</em>. Più povero è, +meglio è.</p> +</div> +<div class="section" id="moduli-e-packages"> +<h2><a class="toc-backref" href="#id35" name="moduli-e-packages">Moduli e packages</a></h2> +<p>Qualunque file Python può essere visto come un modulo. Un package invece, +è una directory contente un file <tt class="docutils literal"><span class="pre">__init__.py</span></tt> che contiene il codice di +inizializzazione (<tt class="docutils literal"><span class="pre">__init__.py</span></tt> può benissimo essere vuoto, oppure può +consistere solo di 'import' dei moduli di quel package).</p> +<p>Importare un package non importa automaticamente tutti i suoi moduli, +a meno che questo non sia detto esplicitamente nell' <tt class="docutils literal"><span class="pre">__init__.py</span></tt>.</p> +<p>I package possono essere nidificati senza limite.</p> +<p>Le variabili globali di un modulo sono locali a quel modulo, quindi +non c'è mai il rischio di fare danni (a meno che un modulo non importi +esplicitamente una variabile corrispondente ad un oggetto mutabile e +la cambi esplicitamente).</p> +<p>E' possibile mettere un package nel Python path automaticamente per +tutti gli utenti listando il nome del package in un file <tt class="docutils literal"><span class="pre">.pth</span></tt> nella +directory site-packages (vedere la documentazione di distutils).</p> +</div> +<div class="section" id="come-si-documenta-una-libreria-python"> +<h2><a class="toc-backref" href="#id36" name="come-si-documenta-una-libreria-python">Come si documenta una libreria Python</a></h2> +<p>Python è un linguaggio "self-documenting" grazie alle doctrings. +Usatele. In più guardate come è documentato un progetto Python +tipico (io suggerisco di guardare a <em>docutils</em>) e copiate le convenzioni +usate lì. Ci sono dei file standard come il README.txt, l'HISTORY.txt, +il BUGS.txt, una directory docs, una directory test, un file setup.py, +etc.</p> +<p>Tenete presente che grazie a <em>doctest</em> potete inserire dei test automatici +all'interno della vostra documentazione.</p> +</div> +</div> +<div class="section" id="modulo-6-domande-estemporanee"> +<h1><a class="toc-backref" href="#id37" name="modulo-6-domande-estemporanee">Modulo 6: Domande estemporanee</a></h1> +<p><em>Risponderò alle domande dell'audience, anche al di fuori dal programma, +se di interesse generale</em>.</p> +<div class="section" id="come-funziona-import"> +<h2><a class="toc-backref" href="#id38" name="come-funziona-import">Come funziona 'import'</a></h2> +<p>Se un modulo è già stato importato e chiamate 'import' una seconda +volta, il modulo NON viene importato due volte. Questo può essere +un problema nell'interprete interattivo. Se importate un modulo, +poi lo modificate e lo importate di nuovo, vi resterà in memoria +la copia <em>vecchia</em>, quella non modificata. La soluzione è usare +'reload', che vi importerà veramente la nuova versione.</p> +</div> +<div class="section" id="come-funziona-del"> +<h2><a class="toc-backref" href="#id39" name="come-funziona-del">Come funziona '__del__'</a></h2> +<pre class="doctest-block"> +>>> a = 1 +>>> del a +</pre> +<p>vi cancella il nome dal namespace:</p> +<pre class="doctest-block"> +>>> a +Traceback (most recent call last): + File "<stdin>", line 1, in ? +NameError: name 'a' is not defined +</pre> +<p>Tuttavia l'oggetto cui si riferisce <em>non viene cancellato immediatamente</em>. +Verrà cancellato dal garbage collector <em>quando serve</em>, ma soltanto +<em>se non vi sono altre referenze</em> all'oggetto da qualche altra parte. +Inoltre per oggetti C, dovrete definirvi un metodo <tt class="docutils literal"><span class="pre">__del__</span></tt> custom +che ha accesso all'oggetto a livello C. Tenete anche conto che tipicamente +il debugger tiene referenze agli oggetti aggiuntive, quindi in modalità +di debugger i problemi con oggetti non cancellati peggiorano.</p> +<p>Ecco un esempio di un metodo <tt class="docutils literal"><span class="pre">__del__</span></tt> custom piuttosto banale:</p> +<pre class="doctest-block"> +>>> class C(object): +... def __del__(self): +... print 'Hai usato del' +... +>>> c = C() +>>> del c +Hai usato del +</pre> +<p>Il metodo <tt class="docutils literal"><span class="pre">__del__</span></tt> viene chiamato automaticamente all'uscita +dall'interprete:</p> +<pre class="doctest-block"> +>>> class C(object): +... def __del__(self): +... print 'Hai usato del' +... +>>> c = C() +>>> <CTRL-Z> per uscire dall'interprete +Hai usato del +</pre> +<p>In principio, all'uscita del programma Python +<em>dovrebbe chiamare ``__del__`` automaticamente anche se vi sono eccezioni</em>:</p> +<pre class="literal-block"> +#<del_with_exc.py> + +class C(object): + def __del__(self): + print "Hai chiamato del" + +c = C() +raise RuntimeError("Ahi ahi!") + +#</del_with_exc.py> + +$ python del_with_exc.py +Traceback (most recent call last): + File "del_with_exc.py", line 6, in ? + raise RuntimeError("Ahi ahi!") +RuntimeError: Ahi ahi! +Hai chiamato del +</pre> +<p>Tuttavia per oggetti <tt class="docutils literal"><span class="pre">C</span></tt> ed eccezioni le cose possono essere delicate ed +è sempre meglio chiamare <tt class="docutils literal"><span class="pre">del</span></tt> <em>esplicitamente</em>, magari in un +<tt class="docutils literal"><span class="pre">try</span> <span class="pre">..</span> <span class="pre">finally</span></tt>.</p> +</div> +<div class="section" id="che-differenza-c-fra-variabili-di-classe-e-di-istanza"> +<h2><a class="toc-backref" href="#id40" name="che-differenza-c-fra-variabili-di-classe-e-di-istanza">Che differenza c'è fra variabili di classe e di istanza</a></h2> +<p>Questo esempio dovrebbe chiarire la differenza:</p> +<pre class="doctest-block"> +>>> class C(object): +... pippo = 'sono una variabile di classe' +... def __init__(self): +... self.poppi = 'sono una variabile di istanza' +... +</pre> +<p>Le variabili di classe sono le stesse per tutte le istanze:</p> +<pre class="doctest-block"> +>>> c1 = C() +>>> c2 = C() +>>> c1.pippo +'sono una variabile di classe' +>>> c2.pippo +'sono una variabile di classe' +</pre> +<p>Se cambio una variabile di classe, cambia per tutte le istanze, anche +per quelle che sono state create <em>prima</em> del cambiamento:</p> +<pre class="doctest-block"> +>>> C.pippo = 'adesso la cambio' +>>> c1.pippo +'adesso la cambio' +>>> c2.pippo +'adesso la cambio' +</pre> +<p>Lo stesso vale per i metodi, che non sono altro che variabili di classe.</p> +<p>Le variabili di istanza invece sono indipendenti:</p> +<pre class="doctest-block"> +>>> c1.poppi +'sono una variabile di instanza' +>>> c2.poppi +'sono una variabile di instanza' +>>> c1.poppi = 'cambio la variabile dell'istanza c1' +>>> c1.poppi +'cambio la variabile dell'istanza c1' +>>> c2.poppi # questa rimane quella che era prima +'sono una variabile di instanza' +</pre> +</div> +<div class="section" id="che-cosa-sono-i-metodi-che-iniziano-con"> +<h2><a class="toc-backref" href="#id41" name="che-cosa-sono-i-metodi-che-iniziano-con">Che cosa sono i metodi che iniziano con "__"</a></h2> +<p>Sono metodi protetti. Sono utili per evitare rogne con l'ereditarietà. +Per capire il problema dò un esempio semplice qui di seguito.</p> +<p>Normalmente l'ereditarietà accoppia il padre con il figlio, nel senso che +il figlio può sovrascrivere i metodi del padre, e i metodi del padre andranno +automaticamente a chiamare i metodi del figlio:</p> +<pre class="literal-block"> +class B(object): # padre + def __init__(self): + self.hello() + def hello(self): + print "hello!" + +class C(B): # figlio + def hello(self): + print "cucu!" + +b = B() # stampa 'hello!' +c = C() # stampa 'cucu!' +</pre> +<p>In questo esempio l'__init__ del padre chiama l'hello del figlio. Tuttavia, +a volte uno vuole essere sicuro che l'__init__ del padre chiami l'hello +del padre, e non quello del figlio (sempre per via del principio del +disaccoppiamento). Per questo ci sono i <em>metodi protetti</em>:</p> +<pre class="literal-block"> +class B(object): + def __init__(self): # call the '__hello' method in THIS class + self.__hello() + def __hello(self): + print "hello!" + +class C(B): + def __hello(self): # won't be called by B.__init__ + print "cucu!" + +b = B() # stampa 'hello!' +c = C() # stampe 'hello!' +</pre> +<p>I metodi protetti di tipo <tt class="docutils literal"><span class="pre">__<nome</span> <span class="pre">metodo></span></tt> non vanno confusi con i metodi +privati di tipo <tt class="docutils literal"><span class="pre">_<nome</span> <span class="pre">metodo></span></tt> (un solo underscore):</p> +<ol class="arabic"> +<li><p class="first">i metodi privati non andrebbero chiamati mai, se non sapendo al 100% quello +che si sta facendo, e solo in caso di errori nel design della libreria che +si sta utilizzando;</p> +</li> +<li><p class="first">i metodi protetti possono essere chiamati con tranquillità. Proprio +perchè sono protetti, c'è la garanzia che sovrascrivendoli nella +sottoclasse non si va ad alterare il comportamento ereditato dal padre +(nel nostro esempio l'<tt class="docutils literal"><span class="pre">__init__</span></tt> del padre continuerà a chiamare il +proprio <tt class="docutils literal"><span class="pre">__hello</span></tt>, non l'<tt class="docutils literal"><span class="pre">__hello</span></tt> del figlio). D'altra parte i +metodi definiti nel figlio vedranno solo l'<tt class="docutils literal"><span class="pre">__hello</span></tt> del figlio e +non quello del padre.</p> +</li> +<li><p class="first">i metodi protetti non possono essere chiamati accidentalmente, perchè +per esempio <tt class="docutils literal"><span class="pre">c.__hello()</span></tt> non funziona. Tuttavia è possibile chiamarli, +quindi non sono veramente privati: nel nostro esempio è possibile +chiamare</p> +<p><tt class="docutils literal"><span class="pre">c._B__hello</span></tt> (<tt class="docutils literal"><span class="pre">__hello</span></tt> del padre) oppure +<tt class="docutils literal"><span class="pre">c._C__hello</span></tt> (<tt class="docutils literal"><span class="pre">__hello</span></tt> del figlio)</p> +<p>Siccome bisogna specificare il nome della classe dove il metodo è definito +non c'è il rischio di sbagliarsi (a meno di non essere masochisti e di +non dare lo stesso nome al padre e al figlio).</p> +</li> +</ol> +<p>I metodi protetti vengono usati raramente. Vedere "Python in a Nutshell" +per maggiori informazioni sul loro uso.</p> +</div> +</div> +<div class="section" id="soluzioni-al-questionario-di-ammissione"> +<h1><a class="toc-backref" href="#id42" name="soluzioni-al-questionario-di-ammissione">Soluzioni al questionario di ammissione</a></h1> +<div class="section" id="scrivere-un-programma-che-testa-se-una-stringa-rappresenta-un-numero"> +<h2><a class="toc-backref" href="#id43" name="scrivere-un-programma-che-testa-se-una-stringa-rappresenta-un-numero">Scrivere un programma che testa se una stringa rappresenta un numero</a></h2> +<p>Soluzione standard (vedere anche il tutorial di Python):</p> +<pre class="literal-block"> +try: + int(x) +except ValueError: + print "This is not a number!" +</pre> +<p>Soluzione usando tkSimpleDialog:</p> +<pre class="literal-block"> +from tkSimpleDialog import askfloat +askfloat("Enter a number", "Number:") +</pre> +</div> +<div class="section" id="scrivere-un-programma-che-lista-tutti-i-files-nella-directory-corrente"> +<h2><a class="toc-backref" href="#id44" name="scrivere-un-programma-che-lista-tutti-i-files-nella-directory-corrente">Scrivere un programma che lista tutti i files nella directory corrente</a></h2> +<pre class="literal-block"> +for f in os.listdir("."): + print f +</pre> +</div> +<div class="section" id="listare-tutti-i-files-nelle-sottodirectories-ricorsivamente"> +<h2><a class="toc-backref" href="#id45" name="listare-tutti-i-files-nelle-sottodirectories-ricorsivamente">Listare tutti i files nelle sottodirectories ricorsivamente</a></h2> +<pre class="literal-block"> +for cwd, dirs, files in os.walk("."): + for f in files: + print f +</pre> +</div> +<div class="section" id="calcolare-lo-spazio-occupato-da-tutti-i-files-di-tipo-txt-in-una-directory"> +<h2><a class="toc-backref" href="#id46" name="calcolare-lo-spazio-occupato-da-tutti-i-files-di-tipo-txt-in-una-directory">Calcolare lo spazio occupato da tutti i files di tipo .txt in una directory</a></h2> +<p>[Soluzione svolta al corso 1]</p> +<pre class="literal-block"> +import os + +def get_text_files(d): + for cwd, dirs, files in os.walk(d): + for f in files: + if f.lower().endswith(".txt"): + fullname = os.path.join(cwd, f) + size = os.path.getsize(fullname) + yield fullname, size + +from operator import itemgetter +print sum(map(itemgetter(1), get_text_files("."))) +</pre> +</div> +<div class="section" id="listare-i-files-a-seconda-delle-dimensioni"> +<h2><a class="toc-backref" href="#id47" name="listare-i-files-a-seconda-delle-dimensioni">Listare i files a seconda delle dimensioni</a></h2> +<pre class="literal-block"> +print sorted(get_text_files("."), key=itemgetter(1)) +</pre> +</div> +<div class="section" id="scrivere-un-test-per-verificate-che-una-directory-sia-vuota"> +<h2><a class="toc-backref" href="#id48" name="scrivere-un-test-per-verificate-che-una-directory-sia-vuota">Scrivere un test per verificate che una directory sia vuota</a></h2> +<pre class="literal-block"> +def is_empty(d): + if os.listdir(d): return False + else: return True +</pre> +<p>Sarebbe ridondante scrivere</p> +<pre class="literal-block"> +if os.listdir(d) != []: +</pre> +<p>perchè la lista vuota ha già di per sè un valore booleano "False".</p> +</div> +<div class="section" id="aprire-una-finestrella-contenente-la-scritta-hello-world"> +<h2><a class="toc-backref" href="#id49" name="aprire-una-finestrella-contenente-la-scritta-hello-world">Aprire una finestrella contenente la scritta "hello, world!"</a></h2> +<p>[In maniera portabile]</p> +<p>Soluzione più semplice:</p> +<pre class="literal-block"> +# hellotk.pyw +from tkMessageBox import showinfo +showinfo(message="hello") +</pre> +<p>Soluzione alternativa:</p> +<pre class="literal-block"> +# hellotk.pyw +import Tkinter as t +root = t.Tk() +l = t.Label(text="hello") +l.pack() +root.mainloop() +</pre> +<p>Guardatevi sull'help di ActiveState la differenza tra l'estensione <tt class="docutils literal"><span class="pre">.py</span></tt> +e l'estensione <tt class="docutils literal"><span class="pre">.pyw</span></tt> (dovrebbe essere nelle FAQ).</p> +</div> +<div class="section" id="scaricare-la-pagina-web-http-www-example-com-da-internet"> +<h2><a class="toc-backref" href="#id50" name="scaricare-la-pagina-web-http-www-example-com-da-internet">Scaricare la pagina Web <a class="reference" href="http://www.example.com">http://www.example.com</a> da Internet</a></h2> +<pre class="doctest-block"> +>>> from urllib2 import urlopen +>>> print urlopen('http://www.example.com').read() +</pre> +</div> +<div class="section" id="stampare-a-schermo-una-tavola-delle-moltiplicazioni"> +<h2><a class="toc-backref" href="#id51" name="stampare-a-schermo-una-tavola-delle-moltiplicazioni">Stampare a schermo una tavola delle moltiplicazioni</a></h2> +<p>Soluzione senza generatori:</p> +<pre class="literal-block"> +#<maketable.py> + +# non graphic +N = 10 +for i in range(1, N+1): + for j in range(1, N+1): + print "%4d" % (i*j), + print + +# HTML +def maketable(iterable, N): + iterable = iter(iterable) + print "<table border='1'>" + stop = False + while not stop: + print "<tr>" + for j in range(1, N+1): + try: + print "<td>%s</td>" % iterable.next(), + except StopIteration: + print "<td></td>" + stop = True + print "</tr>" + print "</table>" + +import tempfile, webbrowser, os, sys + +def showtable(iterable, N): + stdout = sys.stdout # shows how to redirect stdout correctly + fd, name = tempfile.mkstemp(suffix=".html") + sys.stdout = os.fdopen(fd, "w") + maketable(iterable, N) + webbrowser.open(name) + sys.stdout = stdout + +showtable((i*j for j in range(1, N+1) for i in range(1, N+1)), N) + +#</maketable.py> +</pre> +<p>Soluzione usando i generatori, non HTML:</p> +<pre class="literal-block"> +#<gentable.py> + +def gentable(N): + for i in range(1, N+1): + for j in range(1, N+1): + yield i*j + +def printtable(lst, N): + for i, el in enumerate(lst): + print "%4d" % el, + if (i+1) % 10 == 0: + print + +if __name__ == "__main__": + printtable(gentable(10), 10) + +#</gentable.py> +</pre> +</div> +<div class="section" id="trovare-tutte-le-immagini-jpg-nel-vostro-hard-disk-e-mostrarle-a-schermo-in-formato-ridotto"> +<h2><a class="toc-backref" href="#id52" name="trovare-tutte-le-immagini-jpg-nel-vostro-hard-disk-e-mostrarle-a-schermo-in-formato-ridotto">Trovare tutte le immagini .jpg nel vostro hard disk e mostrarle a schermo in formato ridotto</a></h2> +<p>Soluzione svolta al corso 1:</p> +<pre class="literal-block"> +import os, webbrowser + +def gentableHTML(iterable, N): + iterator = iter(iterable) + yield "<HTML>" + yield "<BODY>" + yield "<TABLE border='1'>" + for i in range(N): + yield "<TR>" + for j in range(N): + yield '<TD><img src="%s" width="50", height="50"></TD>\n' % \ + iterator.next() + yield "</TR>\n" + yield "</TABLE>" + yield "</BODY>" + yield "</HTML>" + +# generatore di file names +def get_files_with_ext(ext_set, d): + """ + ext_set is a set of valid extensions. + """ + assert isinstance(ext_set, set), "%r is not a set!" % ext_set + for cwd, dirs, files in os.walk(d): + for f in files: + name, ext = os.path.splitext(f) + fullname = os.path.join(cwd, f) + if ext.lower() in ext_set and os.path.getsize(fullname): + yield fullname + +if __name__ == "__main__": + # genera il file e lo mostra + images = file("images.html", "w") + for el in gentableHTML(get_files_with_ext(set([".png"]), + "C:\\Documents and Settings"), 15): + print >> images, el, + images.close() + webbrowser.open("images.html") +</pre> +<p>Soluzione più sofisticata, per darvi qualcosa su cui pensare:</p> +<pre class="literal-block"> +#<maketable.py> + +def get_files_with_ext(ext, d): + """ + ext can be a string or a set of strings; for instance + get_files_with_ext(".png") or get_files_with_ext(set(".png", ".gif")) + """ + if not isinstance(ext, set): + ext_set = set([ext]) + for cwd, dirs, files in os.walk(d): + for f in files: + name, ext = os.path.splitext(f) + if ext.lower() in ext_set: + yield os.path.join(cwd, f) + +class Picture(object): + """An example of the utility of __str__""" + def __init__(self, pathname): + self.pathname = pathname + self.name = os.path.basename(pathname) + def __str__(self): + return "<img src=%r width=100>" % self.pathname + +if sys.platform == 'win32': + drive = "C:\\" +else: + drive = "/" +showtable(map(Picture, get_files_with_ext(".jpg", drive)), N) + +#</maketable.py> +</pre> +</div> +</div> +</div> +</body> +</html> + |