summaryrefslogtreecommitdiff
path: root/pypers/marelli/materiale/corso.html
diff options
context:
space:
mode:
Diffstat (limited to 'pypers/marelli/materiale/corso.html')
-rwxr-xr-xpypers/marelli/materiale/corso.html1850
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&#64;gmail.com">michele.simionato&#64;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 &quot;__&quot;</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 &quot;hello, world!&quot;</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 &quot;gracefull exit&quot; 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 &quot;buone&quot;, 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 &quot;How to
+ask smart questions&quot; e ricordatevi sempre che &quot;Google is you friend&quot;.</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">
+&gt;&gt;&gt; print 'documenti\nonna' # \n interpretato come newline
+documenti
+onna
+</pre>
+<p>La soluzione e' usare raw strings:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; print r'documenti\nonna'
+documenti\nonna
+</pre>
+<p>Alternativamente, in versioni recenti di Windows, avreste potuto usare
+anche &quot;/&quot; come separatore:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; import os
+&gt;&gt;&gt; os.path.exists(r'C:\Python24\python.exe')
+True
+&gt;&gt;&gt; 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">
+&gt;&gt;&gt; s = set('pippo')
+&gt;&gt;&gt; s
+set(['i', 'p', 'o'])
+&gt;&gt;&gt; 'i' in s
+True
+&gt;&gt;&gt; 'p' in s
+True
+&gt;&gt;&gt; 'o' in s
+True
+&gt;&gt;&gt; 'z' in s
+False
+</pre>
+<p>E' possibile calcolare l'unione e l'intersezione di insiemi:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; t = set('aglio')
+&gt;&gt;&gt; s | t
+set(['a', 'g', 'i', 'l', 'o', 'p'])
+&gt;&gt;&gt; s &amp; 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">
+&gt;&gt;&gt; a = 1 # i numeri sono immutabili
+&gt;&gt;&gt; b = a
+&gt;&gt;&gt; a += 1
+&gt;&gt;&gt; a
+2
+&gt;&gt;&gt; b
+1
+</pre>
+<pre class="doctest-block">
+&gt;&gt;&gt; a = [1] # le liste sono mutabili
+&gt;&gt;&gt; b = a
+&gt;&gt;&gt; a += [1]
+&gt;&gt;&gt; a
+[1, 1]
+&gt;&gt;&gt; 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">
+#&lt;getattr_ex.py&gt;
+
+class C(object):
+ def m1(self):
+ print &quot;chiamato m1&quot;
+ def m2(self):
+ print &quot;chiamato m2&quot;
+
+if __name__ == &quot;__main__&quot;:
+ c = C()
+ method = raw_input(&quot;Che metodo devo chiamare? [m1 o m2] &quot;)
+ getattr(c, method)()
+
+#&lt;/getattr_ex.py&gt;
+</pre>
+<p><em>Non usate exec quando getattr basterebbe!</em></p>
+<pre class="doctest-block">
+&gt;&gt;&gt; method = 'm1'
+&gt;&gt;&gt; exec 'c.%s()' % method # funziona ma è brutto
+chiamato m1
+&gt;&gt;&gt; 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 =&quot;mplay32&quot;
+
+def play_song(song):
+ subprocess.Popen([PLAYER, &quot;/play&quot;, &quot;/close&quot;, song]) # NON BLOCCA!
+ print &quot;Partito&quot;
+
+
+if __name__ == &quot;__main__&quot;:
+ play_song(&quot;c:/Documents and Settings/micheles/Desktop/Music/1. &quot;
+ &quot;Theme from Harry's Game.mp3&quot;)
+</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">
+#&lt;main.py&gt;
+
+&quot;Chiama due processi proc1a.py e proc1b.py&quot;
+
+import subprocess
+
+CMD_a = [&quot;python&quot;, &quot;-c&quot;, &quot;import proc1a; proc1a.main()&quot;]
+CMD_b = [&quot;python&quot;, &quot;-c&quot;, &quot;import proc1b; proc1b.main()&quot;]
+
+if __name__ == &quot;__main__&quot;:
+ p_a = subprocess.Popen(CMD_a)
+ p_b = subprocess.Popen(CMD_b)
+
+#&lt;/main.py&gt;
+</pre>
+<p>Processo 1a:</p>
+<pre class="literal-block">
+#&lt;proc1a.py&gt;
+
+import time
+
+def main():
+ for i in range(10):
+ print &quot;hello&quot;
+ time.sleep(1)
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc1a.py&gt;
+</pre>
+<p>Processo 1b:</p>
+<pre class="literal-block">
+#&lt;proc1b.py&gt;
+
+#-*- encoding: latin-1 -*-
+import time, sys
+
+def main():
+ try:
+ f = file(&quot;proc1b.py&quot;)
+ for i in range(10):
+ print &quot;world&quot;
+ if i == 5:
+ raise RuntimeError(&quot;Ahia!&quot;)
+ time.sleep(1)
+ finally:
+ f.close()
+ print &quot;Il file è stato chiuso correttamente.&quot;
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc1b.py&gt;
+</pre>
+<p>Ho anche illustrato brevemente come si possono gestire i processi da
+Twisted:</p>
+<pre class="literal-block">
+#&lt;proc2a.py&gt;
+
+&quot;Un processo che genera numeri casuali e li salva nel file data.txt&quot;
+
+import random
+
+def main():
+ ro = random.Random()
+ out = file(&quot;data.txt&quot;, &quot;w&quot;)
+ for number in ro.sample(range(1000), 100):
+ print &gt;&gt; out, number
+ out.close()
+ print &quot;Dati salvati sul file 'data.txt'&quot;
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc2a.py&gt;
+
+#&lt;proc2b.py&gt;
+
+&quot;Un processo che genera l'istogramma histo.png dai dati in data.txt&quot;
+
+from pylab import hist, savefig
+
+def main():
+ hist([int(n) for n in file(&quot;dat.txt&quot;)], 10)
+ savefig(&quot;histo.png&quot;)
+ print &quot;Istogramma salvato sul file 'histo.png'&quot;
+
+if __name__ == &quot;__main__&quot;:
+ main()
+
+#&lt;/proc2b.py&gt;
+
+#&lt;twisted_main.py&gt;
+
+&quot;Il main che chiama proc2a.py e proc2b.py nell'ordine e gestisce gli errori&quot;
+
+import webbrowser, sys
+if sys.platform == &quot;win32&quot;:
+ 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(&quot;histo.png&quot;)
+
+def genera_histo(out_di_genera_dati):
+ print out_di_genera_dati
+ getProcessOutput(sys.executable, (r&quot;c:\corso\processi\proc2b.py&quot;,)) \
+ .addCallback(visualizza_histo) \
+ .addErrback(scrivi_messaggio)
+
+def genera_dati():
+ getProcessOutput(sys.executable, (r&quot;c:\corso\processi\proc2a.py&quot;,)) \
+ .addCallback(genera_histo) \
+ .addErrback(scrivi_messaggio)
+
+if __name__ == &quot;__main__&quot;:
+ reactor.callLater(0, genera_dati) # call &quot;genera_dati&quot; after 0 seconds
+ reactor.run()
+
+#&lt;/twisted_main.py&gt;
+</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 &quot;for&quot;; un iteratore è un oggetto con un metodo .next().
+Il modo più comune per definire iteratori è tramite un generatore,
+cioè una &quot;funzione&quot; con uno &quot;yield&quot;:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; def gen123():
+... yield 1
+... yield 2
+... yield 3
+...
+&gt;&gt;&gt; it = gen123()
+&gt;&gt;&gt; it.next()
+1
+&gt;&gt;&gt; it.next()
+2
+&gt;&gt;&gt; it.next()
+3
+&gt;&gt;&gt; it.next()
+Traceback (most recent call last):
+ File '&lt;stdin&gt;', line 1, in ?
+StopIteration
+</pre>
+<p>Un ciclo &quot;for&quot; internamente converte l'iterabile in un iteratore,
+chiama il metodo &quot;.next()&quot; successivamente e trappa l'eccezione StopIteration,
+uscendo dal loop quando non c'è più nulla su cui iterare:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; it = gen123()
+&gt;&gt;&gt; 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">
+&gt;&gt;&gt; try:
+... raise RuntimeError(&quot;Ahia&quot;)
+... finally:
+... print &quot;Io vengo eseguito SEMPRE, anche se c'è un'eccezione!&quot;
+...
+Io vengo eseguito SEMPRE, anche se c'e' un'eccezione!
+Traceback (most recent call last):
+ File '&lt;stdin&gt;', 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">
+&gt;&gt;&gt; def div2(x):
+... assert isinstance(x, int), '%s non è un numero intero' % x
+... return x/2
+...
+&gt;&gt;&gt; div2(14)
+7
+&gt;&gt;&gt; div2(14.0)
+Traceback (most recent call last):
+ File '&lt;stdin&gt;', line 1, in ?
+ File '&lt;stdin&gt;', line 2, in div2
+AssertionError: 14.0 non è un numero intero
+</pre>
+<p>Tipicamente si usa in &quot;sanity checks&quot;, 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(&quot;myscript.py&quot;).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 &quot;myscript.py&quot;)
+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(&quot;myscript.py&quot;, 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">
+#&lt;exc_debug.py&gt;
+
+# 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
+
+#&lt;/exc_debug.py&gt;
+</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">
+#&lt;example.py&gt;
+import exc_debug
+a = 1
+b = 0
+a/b
+#&lt;/example.py&gt;
+</pre>
+<p>fa partire il debugger:</p>
+<pre class="literal-block">
+$ python example.py
+Traceback (most recent call last):
+ File &quot;example.py&quot;, line 4, in ?
+ a/b
+ZeroDivisionError: integer division or modulo by zero
+
+&gt; /mnt/hda2/cygwin/home/micheles/md/pypers/marelli/materiale/example.py(4)?()
+-&gt; 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">
+#&lt;esempio1.py&gt;
+
+import threading, time, sys
+
+def print_hello():
+ for i in range(10):
+ print &quot;hello&quot;
+ if i == 5:
+ raise RuntimeError(&quot;Problema a runtime&quot;)
+ time.sleep(1)
+
+def print_world():
+ for i in range(10):
+ print &quot;world&quot;
+ time.sleep(1)
+
+threading.Thread(target=print_hello).start()
+threading.Thread(target=print_world).start()
+
+#&lt;/esempio1.py&gt;
+</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 &quot;/usr/lib/python2.4/threading.py&quot;, line 442, in __bootstrap
+ self.run()
+ File &quot;/usr/lib/python2.4/threading.py&quot;, line 422, in run
+ self.__target(*self.__args, **self.__kwargs)
+ File &quot;esempio1.py&quot;, line 7, in print_hello
+ raise RuntimeError(&quot;Problema a runtime&quot;)
+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 &quot;hello&quot;
+ if i == 5:
+ try:
+ raise RuntimeError(&quot;Problema a runtime&quot;)
+ except RuntimeError, e:
+ END = True
+ time.sleep(1)
+
+def print_world():
+ i = 0
+ while not END:
+ print &quot;world&quot;
+ 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">
+&quot;Esempio di due threads comunicanti tramite una queue.&quot;
+
+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 == &quot;terminate&quot;: break
+ print &quot;%s, hello&quot; % i
+ time.sleep(1)
+
+def print_world():
+ for i in range(10):
+ print &quot;%s, world&quot; % i
+ if i == 5:
+ queue.put(&quot;terminate&quot;) # manda il messaggio di terminazione
+ root.quit()
+ raise RuntimeError(&quot;Errore nel thread print_world!&quot;)
+ 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">
+#&lt;isnumber.py&gt;
+
+def is_number(arg):
+ &quot;Verifica se la stringa arg è un numero valido&quot;
+ try:
+ float(arg)
+ except ValueError:
+ return False
+ else:
+ return True
+
+#&lt;/isnumber.py&gt;
+</pre>
+<p>dal file di test, che convenzionalmente ha un nome che inizia per &quot;test&quot;:</p>
+<pre class="literal-block">
+#&lt;test_isnumber.py&gt;
+
+import unittest
+
+from isnumber import is_number
+
+class TestIsNumber(unittest.TestCase):
+
+ def setUp(self):
+ print &quot;sto inizializzando&quot;
+
+ # test positivi
+ def test_1(self):
+ &quot;Testa che '1' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;1&quot;))
+ def test_2(self):
+ &quot;Testa che '1.3' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;1.3&quot;))
+ def test_3(self):
+ &quot;Testa che '+1.3' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;+1.3&quot;))
+ def test_4(self):
+ &quot;Testa che '-1.3' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;-1.3&quot;))
+
+ # test negativi
+ def test_5(self):
+ &quot;Testa che '1-.3' non è un numero buono.&quot;
+ self.assertFalse(is_number(&quot;1-.3&quot;))
+ def test_6(self):
+ &quot;Testa che 'à non è un numero buono.&quot;
+ self.assertFalse(is_number(&quot;a&quot;))
+ def test_7(self):
+ &quot;Testa che '42' è un numero buono.&quot;
+ self.assertTrue(is_number(&quot;42&quot;))
+
+ def tearDown(self):
+ print &quot;Sto chiudendo quello che c'è da chiudere&quot;
+
+if __name__ == &quot;__main__&quot;:
+ unittest.main()
+
+#&lt;/test_isnumber.py&gt;
+</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">
+#&lt;test_exc.py&gt;
+
+import unittest
+
+def divide(a, b):
+ return a/b
+
+class TestIsNumber(unittest.TestCase):
+ def test_1(self):
+ &quot;Divide 4/2&quot;
+ self.assertEqual(divide(4,2), 2)
+ def test_2(self):
+ &quot;Divide 4/0&quot;
+ self.assertRaises(ZeroDivisionError, divide, 4, 0)
+
+
+if __name__ == &quot;__main__&quot;:
+ unittest.main()
+
+#&lt;/test_exc.py&gt;
+
+$ 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">
+#&lt;esempio_banale.py&gt;
+
+def sum12():
+ &quot;&quot;&quot;Questa funzione ritorna la somma di 1 + 2::
+ &gt;&gt;&gt; sum12()
+ 3&quot;&quot;&quot;
+ return 1+2
+
+if __name__ == &quot;__main__&quot;:
+ import doctest; doctest.testmod()
+
+#&lt;/esempio_banale.py&gt;
+</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">
+#&lt;doctest_runner.py&gt;
+
+import doctest
+
+if __name__== &quot;__main__&quot;:
+ doctest.testfile(&quot;test_isnumber.txt&quot;)
+
+#&lt;/doctest_runner.py&gt;
+</pre>
+<p>Contenuto di 'test_isnumber.txt':</p>
+<pre class="literal-block">
+Questa è la documentazione della funzione isnumber
+====================================================
+
+Esempi di uso:
+
+&gt;&gt;&gt; from isnumber import is_number
+&gt;&gt;&gt; is_number(&quot;1&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;1.3&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;+1.3&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;-1.3&quot;)
+True
+&gt;&gt;&gt; is_number(&quot;1-.3&quot;)
+False
+&gt;&gt;&gt; is_number(&quot;a&quot;)
+False
+&gt;&gt;&gt; is_number(&quot;42&quot;)
+True
+&gt;&gt;&gt; 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(&quot;1&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;1.3&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;+1.3&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;-1.3&quot;)
+Expecting:
+ True
+ok
+Trying:
+ is_number(&quot;1-.3&quot;)
+Expecting:
+ False
+ok
+Trying:
+ is_number(&quot;a&quot;)
+Expecting:
+ False
+ok
+Trying:
+ is_number(&quot;42&quot;)
+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__== &quot;__main__&quot;:
+ 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 = &quot;C:/Documents and Settings/micheles/Desktop/Music&quot;
+
+def play_song(name):
+ po = subprocess.Popen([&quot;mplay32&quot;, &quot;/play&quot;, &quot;/close&quot;, name])
+ return po.pid
+
+class InterpreteDiComandi(cmd.Cmd):
+ cwd = MUSICDIR
+ prompt = &quot;Player&gt; &quot;
+ def preloop(self):
+ self.process_list = []
+ os.chdir(MUSICDIR)
+
+ def do_play(self, arg):
+ &quot;Suona una canzone&quot;
+ if not arg:
+ print &quot;Per favore scrivi il nome di una canzone!&quot;
+ else:
+ self.process_list.append(play_song(arg))
+
+ def do_quit(self, dummy):
+ &quot;Esce dall'interprete.&quot;
+ return True
+
+ def safe_kill(self, pid):
+ try:
+ os.kill(pid)
+ except WindowsProcessDidNotStartCorrectly, e:
+ print e
+
+ def do_kill(self, arg):
+ &quot;Uccide il player&quot;
+ try:
+ pid = self.process_list.pop()
+ except IndexError:
+ print &quot;Hai già ucciso tutti i processi!&quot;
+ else:
+ self.safe_kill(pid)
+
+ def postloop(self):
+ for pid in self.process_list:
+ self.safe_kill(pid)
+ print &quot;Ho ucciso tutto!&quot;
+ os.chdir(os.path.dirname(os.path.abspath(__file__)))
+
+ # metodi che possono essere utili per il debugging
+ def do_print_dir(self, arg):
+ &quot;Comando utile per il debugging&quot;
+ print self.cwd
+
+ def do_raise_exc(self, dummy):
+ raise RuntimeError(&quot;Tanto per vedere che succede&quot;)
+
+ def do_sleep(self, arg):
+ &quot;utile per vedere quello che i test automatici stanno facendo&quot;
+ 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 &lt; 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 &quot;in the large&quot;. 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">
+&gt;&gt;&gt; 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 &quot;self-documenting&quot; 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">
+&gt;&gt;&gt; a = 1
+&gt;&gt;&gt; del a
+</pre>
+<p>vi cancella il nome dal namespace:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; a
+Traceback (most recent call last):
+ File &quot;&lt;stdin&gt;&quot;, 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">
+&gt;&gt;&gt; class C(object):
+... def __del__(self):
+... print 'Hai usato del'
+...
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; 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">
+&gt;&gt;&gt; class C(object):
+... def __del__(self):
+... print 'Hai usato del'
+...
+&gt;&gt;&gt; c = C()
+&gt;&gt;&gt; &lt;CTRL-Z&gt; 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">
+#&lt;del_with_exc.py&gt;
+
+class C(object):
+ def __del__(self):
+ print &quot;Hai chiamato del&quot;
+
+c = C()
+raise RuntimeError(&quot;Ahi ahi!&quot;)
+
+#&lt;/del_with_exc.py&gt;
+
+$ python del_with_exc.py
+Traceback (most recent call last):
+ File &quot;del_with_exc.py&quot;, line 6, in ?
+ raise RuntimeError(&quot;Ahi ahi!&quot;)
+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">
+&gt;&gt;&gt; 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">
+&gt;&gt;&gt; c1 = C()
+&gt;&gt;&gt; c2 = C()
+&gt;&gt;&gt; c1.pippo
+'sono una variabile di classe'
+&gt;&gt;&gt; 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">
+&gt;&gt;&gt; C.pippo = 'adesso la cambio'
+&gt;&gt;&gt; c1.pippo
+'adesso la cambio'
+&gt;&gt;&gt; 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">
+&gt;&gt;&gt; c1.poppi
+'sono una variabile di instanza'
+&gt;&gt;&gt; c2.poppi
+'sono una variabile di instanza'
+&gt;&gt;&gt; c1.poppi = 'cambio la variabile dell'istanza c1'
+&gt;&gt;&gt; c1.poppi
+'cambio la variabile dell'istanza c1'
+&gt;&gt;&gt; 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 &quot;__&quot;</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 &quot;hello!&quot;
+
+class C(B): # figlio
+ def hello(self):
+ print &quot;cucu!&quot;
+
+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 &quot;hello!&quot;
+
+class C(B):
+ def __hello(self): # won't be called by B.__init__
+ print &quot;cucu!&quot;
+
+b = B() # stampa 'hello!'
+c = C() # stampe 'hello!'
+</pre>
+<p>I metodi protetti di tipo <tt class="docutils literal"><span class="pre">__&lt;nome</span> <span class="pre">metodo&gt;</span></tt> non vanno confusi con i metodi
+privati di tipo <tt class="docutils literal"><span class="pre">_&lt;nome</span> <span class="pre">metodo&gt;</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 &quot;Python in a Nutshell&quot;
+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 &quot;This is not a number!&quot;
+</pre>
+<p>Soluzione usando tkSimpleDialog:</p>
+<pre class="literal-block">
+from tkSimpleDialog import askfloat
+askfloat(&quot;Enter a number&quot;, &quot;Number:&quot;)
+</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(&quot;.&quot;):
+ 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(&quot;.&quot;):
+ 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(&quot;.txt&quot;):
+ 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(&quot;.&quot;)))
+</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(&quot;.&quot;), 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 &quot;False&quot;.</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 &quot;hello, world!&quot;</a></h2>
+<p>[In maniera portabile]</p>
+<p>Soluzione più semplice:</p>
+<pre class="literal-block">
+# hellotk.pyw
+from tkMessageBox import showinfo
+showinfo(message=&quot;hello&quot;)
+</pre>
+<p>Soluzione alternativa:</p>
+<pre class="literal-block">
+# hellotk.pyw
+import Tkinter as t
+root = t.Tk()
+l = t.Label(text=&quot;hello&quot;)
+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">
+&gt;&gt;&gt; from urllib2 import urlopen
+&gt;&gt;&gt; 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">
+#&lt;maketable.py&gt;
+
+# non graphic
+N = 10
+for i in range(1, N+1):
+ for j in range(1, N+1):
+ print &quot;%4d&quot; % (i*j),
+ print
+
+# HTML
+def maketable(iterable, N):
+ iterable = iter(iterable)
+ print &quot;&lt;table border='1'&gt;&quot;
+ stop = False
+ while not stop:
+ print &quot;&lt;tr&gt;&quot;
+ for j in range(1, N+1):
+ try:
+ print &quot;&lt;td&gt;%s&lt;/td&gt;&quot; % iterable.next(),
+ except StopIteration:
+ print &quot;&lt;td&gt;&lt;/td&gt;&quot;
+ stop = True
+ print &quot;&lt;/tr&gt;&quot;
+ print &quot;&lt;/table&gt;&quot;
+
+import tempfile, webbrowser, os, sys
+
+def showtable(iterable, N):
+ stdout = sys.stdout # shows how to redirect stdout correctly
+ fd, name = tempfile.mkstemp(suffix=&quot;.html&quot;)
+ sys.stdout = os.fdopen(fd, &quot;w&quot;)
+ 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)
+
+#&lt;/maketable.py&gt;
+</pre>
+<p>Soluzione usando i generatori, non HTML:</p>
+<pre class="literal-block">
+#&lt;gentable.py&gt;
+
+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 &quot;%4d&quot; % el,
+ if (i+1) % 10 == 0:
+ print
+
+if __name__ == &quot;__main__&quot;:
+ printtable(gentable(10), 10)
+
+#&lt;/gentable.py&gt;
+</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 &quot;&lt;HTML&gt;&quot;
+ yield &quot;&lt;BODY&gt;&quot;
+ yield &quot;&lt;TABLE border='1'&gt;&quot;
+ for i in range(N):
+ yield &quot;&lt;TR&gt;&quot;
+ for j in range(N):
+ yield '&lt;TD&gt;&lt;img src=&quot;%s&quot; width=&quot;50&quot;, height=&quot;50&quot;&gt;&lt;/TD&gt;\n' % \
+ iterator.next()
+ yield &quot;&lt;/TR&gt;\n&quot;
+ yield &quot;&lt;/TABLE&gt;&quot;
+ yield &quot;&lt;/BODY&gt;&quot;
+ yield &quot;&lt;/HTML&gt;&quot;
+
+# generatore di file names
+def get_files_with_ext(ext_set, d):
+ &quot;&quot;&quot;
+ ext_set is a set of valid extensions.
+ &quot;&quot;&quot;
+ assert isinstance(ext_set, set), &quot;%r is not a set!&quot; % 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__ == &quot;__main__&quot;:
+ # genera il file e lo mostra
+ images = file(&quot;images.html&quot;, &quot;w&quot;)
+ for el in gentableHTML(get_files_with_ext(set([&quot;.png&quot;]),
+ &quot;C:\\Documents and Settings&quot;), 15):
+ print &gt;&gt; images, el,
+ images.close()
+ webbrowser.open(&quot;images.html&quot;)
+</pre>
+<p>Soluzione più sofisticata, per darvi qualcosa su cui pensare:</p>
+<pre class="literal-block">
+#&lt;maketable.py&gt;
+
+def get_files_with_ext(ext, d):
+ &quot;&quot;&quot;
+ ext can be a string or a set of strings; for instance
+ get_files_with_ext(&quot;.png&quot;) or get_files_with_ext(set(&quot;.png&quot;, &quot;.gif&quot;))
+ &quot;&quot;&quot;
+ 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):
+ &quot;&quot;&quot;An example of the utility of __str__&quot;&quot;&quot;
+ def __init__(self, pathname):
+ self.pathname = pathname
+ self.name = os.path.basename(pathname)
+ def __str__(self):
+ return &quot;&lt;img src=%r width=100&gt;&quot; % self.pathname
+
+if sys.platform == 'win32':
+ drive = &quot;C:\\&quot;
+else:
+ drive = &quot;/&quot;
+showtable(map(Picture, get_files_with_ext(&quot;.jpg&quot;, drive)), N)
+
+#&lt;/maketable.py&gt;
+</pre>
+</div>
+</div>
+</div>
+</body>
+</html>
+