Il 0% ha trovato utile questo documento (0 voti)
8 visualizzazioni

Python.corso.scai

Caricato da

Nunzio Castaldi
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
8 visualizzazioni

Python.corso.scai

Caricato da

Nunzio Castaldi
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 512

Python for computational

science
Contenuti

• Cosa è Python
• Linguaggi compilati vs interpretati
• Python per il calcolo scinetifico tecnico
• Esempi
Python sta per Pitone?
• Il nome deriva da “Monty Python's Flying Circus” (famoso
gruppo di comici inglese di cui il padre di Python, GuidoVan
Rossum, è grande fan)
• Ciononostante, numerosi libri e pubblicazioni/progetti di e su
Python usano il pitone come animale simbolo
Cosa è python

“Python è un linguaggio di programmazione di alto livello,


interpretato, orientato agli oggetti e con una semantica dinamica. […]
molto interessante per lo sviluppo rapido di applicazioni, così come
per l’utilizzo come linguaggio di scripting o come linguaggio collante
per connettere assieme componenti esistenti. La sintassi semplice e
facile da apprendere di Python enfatizza la leggibilità e riduce il costo
di mantenimento dei programmi. […] programmazione modulare ed il
riutilizzo del codice. L’interprete Python e l’estesa libreria standard sono
disponibili sia come sorgente che in forma binaria, senza costo per le
maggiori piattaforme, possono inoltre essere ridistribuiti
liberamente.”

TRATTO DA: Manuale di riferimento di Python versione 2.3.4


http://docs.python.it/paper-a4/ref.pdf
In dettaglio
Temi trattati in maniera approfondita:
• linguaggio di alto livello
• interpretato (e relazione con la performance)
• semantica dinamica
• sviluppo rapido di applicazioni
• linguaggio di scripting
• leggibilità
• programmazione modulare
Temi non trattati in maniera approfondita:
• orientato agli oggetti (solo cenni)
• linguaggio collante
Breve storia del linguaggio

• Python è stato creato agli inizi degli anni 90 da Guido van Rossum al
Centro di Matematica di Stichting (CWI, si veda http://www.cwi.nl/)
nei Paesi Bassi, come successore di un linguaggio chiamato ABC.
Guido rimane l’autore principale di Python, anche se molti altri vi
hanno contribuito.
• Nel 1995, Guido continua il suo lavoro su Python presso il Centro
Nazionale di Ricerca (CNRI, si veda http://www.cnri.reston.va.us/) in
Reston, Virginia, dove ha rilasciato parecchie versioni del software.

TRATTO DA: Manuale di riferimento di Python versione 2.3.4


http://docs.python.it/paper-a4/ref.pdf
Breve storia del linguaggio
• Nell’ottobre dello stesso anno, la squadra di PythonLabs
diventa la Digital Creations (ora Zope Corporation; si veda
http://www.zope.com/). Nel 2001 viene fondata la Python
Software Foundation (PSF, si veda
http://www.python.org/psf/), un’organizzazione senza scopo
di lucro creata specificamente per detenere la proprietà
intellettuale di Python. Zope Corporation è un membro
sponsorizzato dalla PSF.

• Tutti i rilasci di Python sono Open Source (si veda


http://www.opensource.org/ per la definizione di “Open
Source”).
Documentazione ufficiale e in italiano

• La documentazione ufficiale del linguaggio la si può scaricare


da:
http://www.python.org/doc/current/

• Esistono numerosi documenti tradotti in Italiano consultabili


da:
http://docs.python.it/
Modulo 1: introduzione al linguaggio
Ordine del modulo
1. Linguaggi interpretati e compilati
2. Caratteristiche generali di python
3. Applicazioni scientifiche: motivazioni
4. Python VS Matlab ®
5. Esempi e considerazioni
Due famiglie di linguaggi

• Linguaggi interpretati vs linguaggi compilati

Interpretato Compilato

Python C/C++

Matlab ® Fortran

Perl Java
Due famiglie di linguaggi
LINGUAGGI LINGUAGGI COMPILATI
INTERPRETATI

codice sorgente codice sorgente

codice codice sorgente


pre-processato compilato

codice
interpretato codice macchina
Interprete VS Compilatore
COMPILATORE
• Il processo di compilazione è costituito da diverse fasi

• La prima fase genera un file object .o a partire dai codici sorgenti .c


• La seconda fase genera l’eseguibile a partire dai file object .o
• Eventuali librerie sono linkate staticamente o dinamicamente a run-
time.
L’eseguibile generato è machine-dependent
Python Virtual Machine
L’interprete è uno strato di software che si pone a metà tra il codice utente e l’HW del
processore che esegue i comandi

L’installazione base di python codice sorgente


prevede infatti almeno 2 (.py)
componenti fondamentali:
- l’interprete
- set di librerie base codice
pre-processato
Python userà I pre-compilati per ogni eseguzione (.pyc)
succesiva se non avvengono modifiche al codice sorgente
(time-stamp)

La PVM è di fatto la componente che esegue a livello


Virtual Machine
macchina le singole istruzioni contenute nel .pyc.
Di fatto è una componente interna al linguaggio che
tramite un grande ciclo itera lungo tutte le istruzioni
Costi e Benefici
• Come visto l’interprete python (con PVM annessa) lavora in buona sostanza come un
compilatore classico. La sola vera differenza risiede nel fatto che il codice, dopo essere
stato pre-processato viene eseguito immediatamente; tuttavia I files .pyc sono diversi dai
file .exe del c/c++.
• Il compilatore infatti richiede uno step aggiuntivo e il tempo di compilazione può variare
considerevolmente in base al livello di ottimizzazione richiesto.
• Il codice eseguibile nel caso di un compilatore risulta generalmente più performante.
• Python in questo senso grazie al meccanismo della PVM si pone a metà via tra i linguaggi
compilati (C/C++) e quelli interpretati classici (senza PVM).

costi
benefici
Costi e Benefici
• Velocità di esecuzione: i linguaggi compilati hanno in generale prestazioni
migliori dei linguaggi interpretati (compilazione ottimizzata)
• Velocità di sviluppo: i linguaggi interpretati sono di alto livello, la messa a
punto del codice è più semplice, sia per semplicità di programmazione, sia
perché gli interpreti permettono di correggere gli errori non appena
vengono scoperti, senza necessità di ricompilazione
• Portabilità del codice: su piattaforme hardware/software diverse da quelle
su cui il codice è stato sviluppato.
– Per un codice compilato:
– Portabilità dell’eseguibile su piattaforme con hardware,
software simile e quello su cui il codice è stato compilato
– Portabilità tramite ricompilazione: esistenza di compilatori e
librerie.
– Per un codice interpretato:
– Esistenza di un interprete sulla nuova piattaforma.
Due famiglie di linguaggi
Definizioni alternative
Possibile definire I linguaggi distinguendoli anche per utilizzo o
per caratteristiche macroscopiche. In questo caso si potrebbe
parlare di linguaggi:

• Type-safe (Fortran C/C++) VS dynamically typed (Python, Perl,


PHP): stando ad indicare che la dichiarazione delle variabile
deve seguire regole opposte nei due casi
• System VS scripting: stando ad indicare la natura dell’utilizzo
tipico
• High level VS low level: stando ad indicare livelli di
vicinanza/lontananza dalla macchina e conseguenti livelli di
astrazione
Tipizzazione
• Tipizzazione statica:
Alcuni linguaggi come il C/C++ o il Fortran adottano una tipizzazione statica.
A ciascuna variabile è associato un tipo nell’atto della dichiarazione e non
può cambiare nell’ambito dello scope di quel nome.
Una variabile può essere associato a diversi oggetti nell'ambito del suo
scope, purché questi oggetti abbiano tutti lo stesso tipo.
Esempio:
void prova(){
float a=5.0;
}
int main(){
int a=0;
int b=5;
b=a;
}
Tipizzazione
• Tipizzazione dinamica:
Nella maggior parte dei linguaggi interpretati la tipizzazione è dinamica.
Non c’è necessità di dichiarare il tipo associato ad una variabile.
Una variabile può essere associata a più dati differenti.
>>> a = 4
>>> print type(a)
<type 'int'>
>>> a = 4.5
>>> print type(a)
<type 'float'>
>>> a = "sono una stringa"
>>> print type(a)
<type 'str'>
>>> a = 1, 2
>>> print type(a)
<type 'tuple'>
Tipizzazione
• Tipizzazione Forte
Le espressioni a cui un oggetto può prendere parte dipendono dal tipo dell’oggetto. Un
tipizzazione forte fa sì che un’operazione effettuata con tipi di dato non coerenti non possa
essere svolta.
>>> a=5
>>> b=4
>>> a+b
9
>>> c=5.6
>>> a+c
10.6
>>> s='9'
>>> a+s
Traceback (most recent call last):
File "<pyshell#195>", line 1, in <module>
a+s
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Tipizzazione
• Tipizzazione Debole:
Linguaggi in cui il tipo di dato può essere ignorato. Operazioni
tra dati diversi sono consentiti.
PERL
Print ’3’’+4

PYTHON
print '3'+4
Traceback (most recent call last):
File "<pyshell#196>", line 1, in <module>
print '3'+4
TypeError: cannot concatenate 'str' and 'int' objects
Duck typing/Nominal Typing
Nominal Typing
• Buona parte dei linguaggi compilati eseguono un typing nominale.
Due oggetti si riferiscono allo steso tipo se le loro dichiarazioni sono identiche. Il tipo è noto a tempo di
compilazione.

Duck Typing
• “if it walks like a duck, and quacks like a duck, then it is a duck”
• Nel duck – typing il controllo sul tipo è fatto solo a tempo di esecuzione
>>> l = [1, 2, 3]
>>> d = { 0: 0, 'a': 2, 'b': 4 }
>>> i = 4
>>> print l[0] + 3, d[0]+3
(4,3)
>>> print i[0] + 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is unsubscriptable
The Zen of Python
• Python è un linguaggio di programmazione con molti aspetti
positivi:
– Grande semplicità d'uso
– Grande semplicità di apprendimento (assomiglia alla

pseudocodifica)
– Grande leggibilità (c'è un solo modo per fare qualsiasi cosa)
– Grande portabilità.

• Python è un linguaggio multiparadigma


– Imperative
– Objectoriented
– Functional
– Structural
– Aspectoriented

NON E’ UN SEMPLICE LINGUAGGIO DI SCRIPTING!!


Python in ambito scientifico

• Questo corso tratta di script come strumento di lavoro nella


programmazione in ambito scientifico tecnico attraverso Python.
Python in ambito scientifico
• Necessita di una conoscenza di base del linguaggio
Scripting interpretati e applicazioni
scientifiche: motivazioni

Esistono tratti comuni a molti linguaggi/ambienti di sviluppo in ambito


scientifico (Maple, Mathematica, Matlab, S-Plus/R, Python) che
hanno invogliato gli sviluppatori in ambito scientifico ad utilizzarli:
• sintassi più semplice e pulita
• connessione diretta tra simulazioni e visualizzazione

Tuttavia non tutti questi ambienti presentano sufficiente


facilità di comunicazione con altri linguaggi/ambienti in ambito
scientifico.

Python sì
Python per applicazioni scientifiche
• Python Success Stories

AstraZeneca Uses Python for Collaborative Drug Discovery


MayaVi Uses Python for Scientific Data Visualization
ForecastWatch.com Uses Python To Help Meteorologists
Python in The Blind Audio Tactile mapping System
Python Streamlines Space Shuttle Mission Design
Carmanah Lights the Way with Python
Google
Youtube
… e tanti altri
Python come collante
Python oltre ad offrire le caratteristiche sopracitate presenta
anche le caratteristiche non banali di:
• essere un ottimo collante tra ambienti/librerie/codici sviluppati
in altri linguaggi per l’ambito scientifico (C, C++ e Fortran) e/o
la visualizzazione
• permettere di eseguire con semplicità la ‘traduzione’ tra diversi
formati di grandi quantità di dati gestendo files e cartelle
interagendo con il sistema operativo
• permettere di associare con relativa semplicità delle interfacce
(GUI) alle applicazioni sviluppate
• essere altamente portabile (Unix, Linux, Mac, Windows)
Python Vs Matlab
• Python per l’ambito scientifico presenta molte caratteristiche simili
a Matlab; per i seguenti motivi è però superiore:
• maggiore potenza e flessibilità del linguaggio
• ambiente completamente open e integrabile con applicazioni
esterne
• moduli compatti contenenti numerosi funzioni
• passaggio di funzioni come argomenti semplificato
• gestione semplificata di strutture dati annidate
• Object Oriented Programming (OOP) miglior supporto con C, C++
Fortran
• funzioni scalari che lavorano spesso anche su array senza modifiche
• sorgenti gratuiti e multipiattaforma
Python vs Matlab

Tuttavia va sottolineato che Matlab (essendo specifico al calcolo


scientifico e alla visualizzazione):
• ha una documentazione superiore
• ha un maggior numero di routines per l’algebra lineare e la
soluzione di ODE, l’ottimizzazione, l’analisi dei segnali, etc.
• ha degli strumenti per la visualizzazione 2D/3D migliori e più
stabili.
Statistiche di utilizzo

Tratto da: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html


Trends
Prospettive nei prossimi 10 anni
Difficile fare previsioni, ma i trend dicono che chi sviluppa applicazioni
scientifiche chiede naturalmente nuove caratteristiche di flessibilità
oltre che di performance al linguaggio candidato:
• C++
• Fortran 2003 (OOP)
• Python
• Java
• PHP
• Perl
• Jython
• Ruby
• ….
L’interprete
• Python è un linguaggio interpretato
• L'interprete esegue una compilazione del sorgente in bytecode, che viene poi
eseguito su una virtual machine.
• L’interprete è molto utile anche usato in interattivo.
• Qualsiasi errore possa avvenire nel corso dell'esecuzione dell'interprete in
interattivo, l'interprete sopravvive, anche in caso di SyntaxError:
>>> 50/0
Traceback (most recent call last):
File "<pyshell#199>", line 1, in <module>
50/0
ZeroDivisionError: integer division or modulo by zero
>>> def func()
SyntaxError: invalid syntax
Come passare gli argomenti
#!/usr/bin/env python
import math
infile='mydata.dat'
outfile='myout.dat'
indata = open( infile, 'r') def f(y):
linee=indata.readlines() if y >= 0.0:
indata.close() return y**5*math.exp(-y)
processati=[ ] else:
x=[ ] return 0.0
for el in linee:
valori = el.split()
x.append(float(valori[0])); y = float(valori[1])
processati.append(f(y))

outdata = open(outfile, 'w')


i=0
for el in processati:
outdata.write('%g %12.5e\n' % (x[i],el))
i+=1
outdata.close()
Come passare gli argomenti
possibili varianti sono:
import sys
try:
infile = sys.argv[1]; outfile = sys.argv[2]
except:
print "Richieste di: ",sys.argv[0], "nome file IN e nome file OUT" sys.exit(1)

oppure:

infile = raw_input( “inserire nome file IN: “ )


outfile = raw_input( “inserire nome file OUT: “ )
Import di moduli
• Un file con terminazione .py costituisce un modulo per Python
• Un modulo può contenere qualsiasi tipo di codice python
$ more my.py
def hello_world():
print “Ciao, mondo!”
$ python
>>> import my
>>> dir(my)
['__builtins__', '__doc__', '__file__', '__name__', '__package__',
'hello_world']
>>> print my.__file__
my.py
>>> my.hello_world()
Ciao, mondo!
Import di moduli
• Python è costituito da una serie di moduli, forniti dalla
installazione di base oppure aggiunti esternamente (tipico dei
moduli scientifici e di visualizzazione)
• Per importare i moduli, siano essi di sistema o aggiunti in
seguito, si usa il comando di sistema from/import con le 3
possibili sintassi:

from os import *
from os import
from os import path as PP
import os
import os as O
Esempio-2.0
import os
#importo lo spazio dei nomi del modulo os
>>> os.curdir
’.’
>>> os.getenv('HOME')
'C:\\Documents and Settings\\ponzini.CILEA-DOM.000'
>>> os.listdir('.')
['aboutDialog.py', 'aboutDialog.pyc', 'AutoComplete.py', 'AutoComplete.pyc',
'AutoCompleteWindow.py', 'AutoCompleteWindow.pyc', 'AutoExpand.py',
'AutoExpand.pyc', 'Bindings.py', 'Bindings.pyc', 'CallTips.py', 'CallTips.pyc',
'CallTipWindow.py', 'CallTipWindow.pyc', 'Clas….]
>>> os.defpath
'.;C:\\bin'
Esempio-2.1
import os as O
#importo lo spazio dei nomi del modulo os come O
>>> O.curdir
’.’
>>> O.getenv('HOME')
'C:\\Documents and Settings\\ponzini.CILEA-DOM.000'
>>> O.listdir('.')
['aboutDialog.py', 'aboutDialog.pyc', 'AutoComplete.py', 'AutoComplete.pyc',
'AutoCompleteWindow.py', 'AutoCompleteWindow.pyc', 'AutoExpand.py',
'AutoExpand.pyc', 'Bindings.py', 'Bindings.pyc', 'CallTips.py', 'CallTips.pyc',
'CallTipWindow.py', 'CallTipWindow.pyc', 'Clas….]
>>> O.defpath
'.;C:\\bin'
Esempio-2.3
from os import *
#importo tutto dallo spazio dei nomi del modulo os
>>> curdir
’.’
>>> getenv('HOME')
'C:\\Documents and Settings\\ponzini.CILEA-DOM.000'
>>> listdir('.')
['aboutDialog.py', 'aboutDialog.pyc', 'AutoComplete.py', 'AutoComplete.pyc',
'AutoCompleteWindow.py', 'AutoCompleteWindow.pyc', 'AutoExpand.py',
'AutoExpand.pyc', 'Bindings.py', 'Bindings.pyc', 'CallTips.py', 'CallTips.pyc',
'CallTipWindow.py', 'CallTipWindow.pyc', 'Clas….]
>>> defpath
'.;C:\\bin'
Esempio-2.4

from os import curdir as CC


#importo un elemento specifico con un alias
>>> CC
’.’
>>> from os import getenv as GG
>> GG ('HOME')
'C:\\Documents and Settings\\ponzini.CILEA-DOM.000'
Import di moduli
Moduli possono essere raggruppati in pacchetti, che hanno una struttura
gerarchica rappresentata da directory. Una directory contenente un file
__init__.py, eventualmente vuoto, è un pacchetto. Se la directory contiene
altri pacchetti o moduli, essi sono accessibili come contenuto del pacchetto.

sound/ Toplevel package


__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
__init__.py wavread.py wavwrite.py
aiffread.py aiffwrite.py auread.py
auwrite.py ...
effects/ Subpackage for sound effects
__init__.py echo.py surround.py
reverse.py ...
filters/ Subpackage for filters
__init__.py equalizer.py vocoder.py
karaoke.py ...
Import di moduli

>>> import sound.effects.echo.echofilter


>>> sound.effects.echo.echofilter(...)
>>> import sound.effects.echo
>>> sound.effects.echo.echofilter(...)
>>> from sound.effects import echo
>>> echo.echofilter(...)
>>> from sound.effects.echo import echofilter
>>> echofilter(...)
Commenti

• Tutto è oggetto in python


– cosa è un oggetto? OGGETTO

FUNZIONI

mondo
DATI
esterno
QUERY
Sintassi di un oggetto
Alcune nozioni base sulla sintassi degli oggetti e sulle modalità di accesso ai membri,
prima di procedere formalmente alla definizione delle classi:

• accesso alle funzioni membro:


– nome_oggetto.nome_funzione_membro()
• accesso ai dati membro:
– nome_oggetto.nome_dato_membro
>>> a
[1, 2, 3, 4]
>>> a.count(1)
1
>>> a.__doc__
"list() -> new list\nlist(sequence) -> new list initialized from sequence's items"
>>> a=numpy.array([1,2,3])
>>> a.shape
(3,)
>>> a.dtype
dtype('int32')
Commenti

Anche un modulo è un oggetto:


• quindi possiamo guardare nel modulo e interrogare I suoi dati
membro:
>> import pickle as P
>> print P.__name__
pickle
>> print P.__version__
Commenti

>>> print P.__doc__


Create portable serialized representations of Python objects.

See module cPickle for a (much) faster implementation.


See module copy_reg for a mechanism for registering custom picklers.
See module pickletools source for extensive comments.

Classes:

Pickler
Unpickler
Commenti
Functions:

dump(object, file)
dumps(object) -> string
load(file) -> object
loads(string) -> object

Misc variables:

__version__
format_version
compatible_formats

>>> print P.__version__


$Revision: 38432 $
>>>
Introspezione

• L'introspezione è la capacità di un linguaggio di fornire varie


informazioni sugli oggetti runtime.
• Python ha un ottimo supporto per l'introspezione, a
differenza di
linguaggi come Fortran o C che non ne hanno alcuno, o C++ che
ha un supporto estremamente limitato.
• Usare l’interprete in interattivo per fare introspezione facilita
la comprensione del codice e del linguaggio.
Introspezione
• Esiste un comando di sistema che permette di conoscere tutti I dati e le funzioni contenute in
un modulo:

>> dir (P)


['APPEND', 'APPENDS', 'BINFLOAT', 'BINGET', 'BININT', 'BININT1', 'BININT2', 'BINPERSID', 'BINPUT',
'BINSTRING', 'BINUNICODE', 'BUILD', 'BooleanType', 'BufferType', 'BuiltinFunctionType',
'BuiltinMethodType', 'ClassType', 'CodeType', 'ComplexType', 'DICT', 'DUP', 'DictProxyType',
'DictType', 'DictionaryType', 'EMPTY_DICT', 'EMPTY_LIST', 'EMPTY_TUPLE', 'EXT1', 'EXT2',
'EXT4', 'EllipsisType', 'FALSE', 'FLOAT', 'FileType', 'FloatType', 'FrameType', 'FunctionType',
'GET', 'GLOBAL', 'GeneratorType', 'GetSetDescriptorType', 'HIGHEST_PROTOCOL', 'INST', 'INT',
'InstanceType', 'IntType', 'LIST', 'LONG', 'LONG1', 'LONG4', 'LONG_BINGET', 'LONG_BINPUT',
'LambdaType', 'ListType', 'LongType', 'MARK', 'MemberDescriptorType', 'MethodType',
'ModuleType', 'NEWFALSE', 'NEWOBJ', 'NEWTRUE', 'NONE', 'NoneType',
'NotImplementedType', 'OBJ', 'ObjectType', 'PERSID', 'POP', 'POP_MARK', 'PROTO', 'PUT',
'PickleError', 'Pickler', 'PicklingError', 'PyStringMap', 'REDUCE', 'SETITEM', 'SETITEMS',
'SHORT_BINSTRING', 'STOP', 'STRING', 'SliceType', 'StringIO', 'StringType', 'StringTypes',
'TRUE', 'TUPLE', 'TUPLE1', 'TUPLE2', 'TUPLE3', 'TracebackType', 'TupleType', 'TypeType',
'UNICODE', 'UnboundMethodType', 'UnicodeType', 'Unpickler', 'UnpicklingError',
'XRangeType', '_EmptyClass', '_Stop', '__all__', '__builtins__', '__doc__', '__file__',
'__name__', '__version__', '_binascii', '_extension_cache', '_extension_registry',
'_inverted_registry', '_keep_alive', '_test', '_tuplesize2code', 'classmap',
'compatible_formats', 'decode_long', 'dispatch_table', 'dump', 'dumps', 'encode_long',
'format_version', 'load', 'loads', 'marshal', 'mloads', 're', 'struct', 'sys', 'whichmodule']
Commenti
ovviamente anche una classe di un modulo è un oggetto:
>> dir(P.Unpickler)
['__doc__', '__init__', '__module__', '_instantiate', 'dispatch', 'find_class',
'get_extension', 'load', 'load_append', 'load_appends', 'load_binfloat',
'load_binget', 'load_binint', 'load_binint1', 'load_binint2', 'load_binpersid',
'load_binput', 'load_binstring', 'load_binunicode', 'load_build', 'load_dict',
'load_dup', 'load_empty_dictionary', 'load_empty_list',
'load_empty_tuple', 'load_eof', 'load_ext1', 'load_ext2', 'load_ext4',
'load_false', 'load_float', 'load_get', 'load_global', 'load_inst', 'load_int',
'load_list', 'load_long', 'load_long1', 'load_long4', 'load_long_binget',
'load_long_binput', 'load_mark', 'load_newobj', 'load_none', 'load_obj',
'load_persid', 'load_pop', 'load_pop_mark', 'load_proto', 'load_put',
'load_reduce', 'load_setitem', 'load_setitems', 'load_short_binstring',
'load_stop', 'load_string', 'load_true', 'load_tuple', 'load_tuple1',
'load_tuple2', 'load_tuple3', 'load_unicode', 'marker']
Uso dei dati membro built-in in main

Di questi dati membro propri di ogni modulo si fa largo uso in


python soprattutto per quanto riguarda l’importazione di un
modulo:

if __name__== ‘__main__’:
print ‘Il chiamante sono proprio io’
else:
print ‘sono stato importato da un altro modulo’
help()
Analogamente a quanto mostrato per la funzione di sistema dir()
che ha validità generale per ogni modulo/oggetto, è anche
disponibile una funzione di sistema per conoscere la
documentazione su una generica funzione:

import pickle
>>> help(pickle.dump)
Help on function dump in module pickle:

dump(obj, file, protocol=None)

>>> help(pickle.load)
Help on function load in module pickle:
Type()
• Determinare il tipo di un’oggetto in Python è estremamente
facile: basta usare il comando type.

>>>a=5
>>>type(a)
<type’int’>
>>> l = [1, "alfa", 0.9, (1, 2, 3)]
>>> print [type(i) for i in l]
[<type 'int'>, <type 'str'>, <type
'float'>, <type 'tuple'>]
>>>
Moduli creati dall’utente
• Tutto quanto mostrato sulle funzioni di sistema per l’introspezione dei
moduli, degli oggetti e delle funzioni vale anche per il codice prodotto
dall’utente.
#file mymodule.py
“””this is the documentation section of my module
usually here I explain who is author; what the module does; the functions provided;
lincesing
“””
_eps=0.001
def myfunc(var1,var2):
“”” this function compare two values and print out the largest one:
-IN: var1; var2
-OUT: none
-usage: myfunc(var1,var2)
“””
if var1>var2: print ‘largest value between input:’, var1
elif var1==var2: print ‘the two value are equal’
else: print ‘largest value between input:’, var2
Moduli creati dall’utente
from mymodule import *
#import mymodule
## import mymodule as M
>> dir()
>> ['__builtins__', '__doc__', '__name__', 'myfunc‘,’_eps’]
#dir(mymodule)
help(myfunc)
Help on function myfunc in module __main__:

myfunc(var1, var2)
this function compare two values and print out the largest one:
-IN: var1; var2
-OUT: none
-usage: myfunc(var1,var2)
#help(mymodule.myfunc)
##help(M.myfunc)
Test

• Dall’interprete interattivo importare il modulo math e


scoprirne il contenuto.
• Usare le funzioni help per fare introspezione
IDE
Contenuti

• Interactive Development Environment (IDE) e installazioni


disponibili in ambito scientifico e non:
– IDLE (non specifico)
– Enthought
– Python-XY
– I-Python

• Download e Installazioni
• Hands-on
Installazione Python
• Python può essere utilizzato su diverse piattaforme
Linux,Macintosh, Windows.
• La distribuzione principale di python è scaricabile
http://www.python.org/

In Linux Python solitamente è installato con il sistema operativo

[alinve@lagrange ~]$ which python


/usr/bin/python
[alinve@lagrange ~]$ man python
[alinve@lagrange ~]$ python -V
Python 2.4.3
Installazione Python
• I pacchetti di Python vengono solitamente distribuiti usando il modulo
Distutils.
• L’installazione si fa in pochi passi.
• Installazione del modulo test-1.0.tar.gz

gunzip -c test-1.0.tar.gz | tar xf - # unpacks


into directory test-1.0
cd test-1.0
python setup.py install

• Il comando python setup.py install costruisce e installa i moduli.


• I moduli vengono installati sotto
/pythonHOME/lib/python2.6/site-packages
Installazione Python
• I pacchetti di Python vengono solitamente distribuiti usando il modulo
Distutils.
• L’installazione si fa in pochi passi.
• Installazione del modulo test-1.0.tar.gz

gunzip -c test-1.0.tar.gz | tar xf - # unpacks


into directory test-1.0
cd test-1.0
python setup.py install

• Il comando python setup.py install costruisce e installa i moduli.


• I moduli vengono installati sotto
/pythonHOME/lib/python2.6/site-packages
Installazione Python
• Per specificare ulteriori path di ricerca è possibile modificare
la variabile d’ambiente PYTHONPATH

$ export PYTHONPATH=$HOME
>>> import sys
>>> sys.path
['', '/home/interni/alinve',
'/usr/lib64/python24.zip', '/usr/lib64/python2.4',
'/usr/lib64/python2.4/plat-linux2',
'/usr/lib64/python2.4/lib-tk',
'/usr/lib64/python2.4/lib-dynload',
'/usr/lib64/python2.4/site-packages',
'/usr/lib64/python2.4/site-packages/Numeric',
'/usr/lib64/python2.4/site-packages/PIL',
'/usr/lib64/python2.4/site-packages/gtk-2.0',
'/usr/lib/python2.4/site-packages']
Installazione Python

• Per modificare il prefix path della home di installazione di


Python è si modifichi la variabile PYTHONHOME

• Per vedere la lista dei moduli installati

>>>help('modules')
Please wait a moment while I gather a list
of all available modules...
Installazione base: IDLE
IDLE: è l’interfaccia built-in di Python sviluppata con la libreria
base GUI (tkinter). IDLE ha seguenti caratteristiche:
• 100% puro Python
• cross-platform: windows/linux
• text editor multi-window con multipli undo
• colorazione del testo basato sulla sintassi Python ed
indentazione automatica del testo.
• completamento delle funzioni
• interprete interattivo
• debugger
• necessita della libreria Tk
Installazione with bacteries included
Esistono installazioni specifiche per il calcolo scientifico e le
applicazioni ingegneristiche che contengono già numerosi
moduli di utilità:
• Enthought (largamente la più diffusa e con un livello di
mantenimento e di investimenti significativo)
• L’installazione la si può scaricare dal link:
http://www.enthought.com/

Cosa contiene:
http://www.enthought.com/products/epdlibraries.php
Installazione with bacteries included

Python-XY (di recente sviluppo e molto promettente) esiste per


ora solo per Windows e per Ubuntu/linux
• L’installazione la si può scaricare dal link:
http://www.pythonxy.com/
Installazione with bacteries included
Installazione with bacteries included

• I-Python (la shell interattiva più potente per questo ambito)


• L’installazione la si può scaricare dal link:
http://ipython.scipy.org/
IPython

Caratteristiche:
• Visualizzazione della lista dei metodi e delle proprietà di ogni variabile
premendo il tasto TAB.
• Visualizzazione della documentazione di ogni oggetto e funzione in
maniera semplice e immediata: nome_funzione?<enter>
• Definizione di comandi personalizzati e di alias (es. %alias l ls -la).
• Possibilità di eseguire comandi di sistema (es. !ls) e di recuperare il loro
output sotto forma di stringa o lista (ipython -p pysh, $$v=ls).
• Mantiene uno storico dei comandi eseguiti (%hist, _ih).
• Integrabile nei programmi come utile strumento per il debugging.
• molto altro…
Demo / Hands on

Connessione ai pc linux
• apertura Ipython
• esecuzione comandi
• lancio di script (#!/bin/python/ e PYTHONPATH)
• comando sys.argv[*]
• comando help()
• introspezione (dir module)
• import pylab
• simple-plot
Note: indentazione/blocchi
• python si basa sulla indentazione del codice
• blocchi logicamente connessi hanno lo stesso livello di indentazione
x = 2.3
y = 1.2

def test(x,y):
if x==y: print 'the two number are equal'
elif x > y: print ' the first number is the greater'
else: print ' the former number is the greater'
test(x,y)
for I in range(2,5,1):
for J in range(5,1,-1):
print 'now testing : ', I,J
test(I,J)
Modalità di lancio
Uno script python può essere lanciato in vari modi:
1) [ponzini@prompt] python script.py
(implica che nel PATH o nel PYTHONPATH sia indicata la locazione di
installazione di python)

2) [ponzini@prompt] ./script.py (implica che lo script sia in modalità


eseguibile (chmod +x))

1) dalla shell di i-python

In [3]: !ls *.py

21/10/2009 11.54 376 pie-hours-py.py

In [4]: run pie-hours-py.py


Nota parametri di input: sys.argv

• Uno script python può avere o meno parametri di input necessari alla
corretta esecuzione dello script:

[prompt@ponzini] python script.py parametro_1 parametro_2 parametro_3

• Per prevedere questa modalità di lancio si può fare uso degli appositi
parametri argv[*] contenuti nel modulo sys:
– argv[0]: nome dello script che stiamo eseguendo
– argv[i]: qualsiasi parametro_i
Esempio
# script che prende 2 parametri di input
import sys
usage="""necessita di due parametri di input (param1, param2)
correct usage: python script.py param1 param2"""

if __name__ == '__main__':
if len(sys.argv) < 2:
print 'lo script: ',sys.argv[0],usage
sys.exit(0) # termina dopo aver stampato la stringa di usage
param1 = sys.argv[1]
param2 = sys.argv[2]
print 'uso I due parametri passati al momento dell’invocazione dello
script:\ ',param1, param2
raw_input()
Nel caso si volesse invece avere una interazione con l’utente dello script è possibile
fare uso di una funzione predisposta per accettare parametri a run-time dallo
stdin.
Esempio:
# script che prende 2 parametri di input
import sys
if __name__ == '__main__':
while(True):
print 'PLEASE INSERT AN INTEGER NUMBER IN THE RANGE 0-10'
param1 = raw_input()
if int(param1) in range(11): # notare che raw_input restituisce una stringa
while(True):
print 'PLEASE INSERT A CHAR PARAMETER IN [A,B,C]'
param2 = raw_input()
if param2 in ['A','B','C']:
print 'uso I due parametri passati dall utente: ',param1,
param2
sys.exit()
else: print 'TRY AGAIN PLEASE'
else: print 'TRY AGAIN PLEASE'
input()
Come raw_input ma con un eval(): eval(raw_input(prompt))
Esempio:

# script che prende 2 parametri di input


import sys

if __name__ == '__main__':
while(True):
print 'PLEASE INSERT AN INTEGER NUMBER IN THE RANGE 0-10'
param1 = input() #notare che c’è eval e int() non va messo
if param1 in range(11):
while(True):
print 'PLEASE INSERT A CHAR PARAMETER IN [A,B,C]'
param2 = input() # causa eval bisogna usare il simbolo di carattere
if param2 in ['A','B','C']:
print 'uso I due parametri passati dall utente: ',param1, param2
sys.exit()
else: print 'TRY AGAIN PLEASE'
else: print 'TRY AGAIN PLEASE'
eval()
Come visto le due funzioni sembrano praticamente equivalenti ma eval() che
cosa produce:
>>> help(eval)
eval(...)
eval(source[, globals[, locals]]) -> value

Evaluate the source in the context of globals and locals.


The source may be a string representing a Python expression
or a code object as returned by compile().
The globals and locals are dictionaries, defaulting to the current
globals and locals. If only globals is given, locals defaults to it.

Diventa strategico quando voglio passare da input qualcosa che venga


valutato (es: nome di una funzione).
input() versione2
# script che prende 2 parametri di input; il secondo parametro è il nome di una
funzione

import sys
def f(x):
print x
def g(x):
print -x
if __name__ == '__main__':
while(True):
print 'PLEASE INSERT AN INTEGER NUMBER IN THE RANGE 0-10'
param1 = input()
if param1 in range(11):
while(True):
print 'PLEASE INSERT THE NAME OF A FUNCTION'
param2 = input() #grazie alla presenza di eval ha senso l’istruzione che segue
if param2 in [f,g]:
print 'uso i due parametri passati dall utente: ',param1, param2
param2(param1)
sys.exit()
else: print 'TRY AGAIN PLEASE'
else: print 'TRY AGAIN PLEASE'
Tipi di Dato
Data Types

Python comparato ad altri linguaggi di programmazione sulla


gestione dei tipi di dato:

• Linguaggi staticamente tipati: linguaggi in cui il tipo è noto a


tempo di compilazione. Le variabili vengono associate ad un
tipo tramite una dichiarazione come in c/c++.
• Linguaggi dinamicamente tipati: linguaggi in cui il tipo è noto
a tempo di esecuzione. L’assegnamento permette di
individuare il tipo di variabile.
• Linguaggi fortemente tipati: linguaggi nei quali i tipi sono
sempre imposti. Un intero non può essere trattato come una
stringa senza una conversione esplicita.
• Linguaggi debolmente tipati: linguaggi nei quali i tipi possono
essere ignorati, p.e. PHP.
Data Types
Python è un linguaggio dinamicamente e fortemente tipato.

Esempio

>>> a='123‘ # tipo string vs C/C++ char a[]=‘123’


>>> b=4 # tipo intero
>>> c=b+int(a) # casting ad intero vs PH $a=‘2’ a è stringa
>>> c $a+=1 a è intero
127 # ok
>>> d=b+a # ERRORE!
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
d=b+a
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Identificatori
E’ possibile associare ad un oggetto un nome simbolico che non
necessita di essere dichiarato.

• Gli identificatori sono i nomi dati per identificare qualcosa.


Alcune regole:
– Il primo carattere dell'identificatore deve essere una lettera dell'alfabeto (maiuscola o
minuscola) o un carattere _
– Il resto del nome dell'identificatore può consistere di lettere, caratteri di sottolineatura
('_') o cifre (0-9).
– I nomi degli identificatori sono case-sensitive.
– Esempi di identificatori validi sono i, __mio_nome, nome_23 e a1b2_c3
– Esempi di identificatori non validi sono 2cose, questo è spaziato, mio-nome "questo è
tra virgolette".
– Esistono parole riservate: and, or, else, if, for, while, def, class ect etc.
Identificatori
In Python non esistono dichiarazioni esplicite di variabili. Vengono create nel momento
dell’assegnamento e distrutte all’uscita dello scope.
Per conoscere il tipo di oggetto associato ad una variabile si utilizza la funzione built-in type().
Esempio
>>> a=5
>>> type(a)
<type ‘int’ >
>>>b=3.2
>>>type(b)
<type’float’>
>>>a=7.2
>>>type(a)
<type’float’>
>>>c=a
>>>C=b
>>>c==C
False
Tipi di dato
• Python dispone di due tipologie di dato, dati semplici:
– Int
– Long
– Float
– Complex
– String

E contenitori.
• Uno dei punti di forza di Python è nei contenitori disponibili,
che sono molto efficienti, comodi da usare, e versatili:
– tuple ()
– list []
– dict {}
– set
Tipo Numerico
Il linguaggio mette a disposizione quattro tipi numerici di dato :

• Integer
Il tipo int è valido per tutti i numeri interi che sono compresi tra
[-2147483648 e 2147483647], vedi (sys.maxint)
Un intero può essere espresso in base decimale oppure in base
esadecimale o ottale antecedendo al numero 0x e 0
rispettivamente:

Esempio
>>> a=300 #decimale
>>> b=0x12c #esadecimale
>>> c=0454 #ottale
>>> a_oct=oct(a) #ottale
>>> a_hex=hex(a) #esadecimale
Tipo Numerico
• Long Integer
Il tipo long è analogo al tipo intero con l’unica eccezione che il valore massimo e
minimo che può assumere è limitato solo dalla memoria a disposizione.

Esempio
>>> a = 1254546699L # il suffisso L indica un long type
>>> b = 484564848766
>>> b
>>> 484564848766L
>>>2**1024
17976931348623159077293051907890247336179769789423065727343008115
77326758055009631327084773224075360211201138798713933576587897688
14416622492847430639474124377767893424865485276302219601246094119
45308295208500576883815068234246288147391311054082723716335051068
4586298239947245938479716304835356329624224137216L

Possono contenere interi di qualsiasi dimensione!!


Tipo Numerico

ESEMPIO

>>>a=2147483647
>>>type(a)
<type 'int'>
>>>a+=1
>>>type(a)
<type 'long'>

Gli int sono automaticamente trasformati in long quando


necessario.
Tipo Numerico
• Floating Point Number

Il tipo float rappresenta numeri reali in doppia precisione


esempio:
>>> a = 12.456
>>> c = 12232e-2
>>> b = .2
>>> 6.12244e-5
Attenzione nell’utilizzo di interi e float!!
Cosa Succede se eseguiamo le seguenti operazioni??
100/3
100//3
100.0/3
100.0//3
100%3
divmod(100,3)
Tipo Numerico
Il prompt di Python di default utilizza la funzione built-in repr() per il display di numeri floating
point, che per default approssima alla 17-esima cifra significativa.
Quando si necessita maggiore accuratezza (p.e. applicazioni finanziarie) è possibile ricorrere
all’utilizzo del modulo Decimal: la precisione è specificata dall’utente e i numeri frazionari
binari sono rappresentati esattamente a scapito di un più lento processamento dei dati.

• Complex Number
Un numero complex rappresenta un tipo numerico complesso in doppia precisione. Si accede
alla parte reale e immaginaria di un numero complesso attraverso le funzioni ‘real’ e
‘imag’.
esempio
>>>r=12+5j
>>>r.imag
5.0
>>>type(r.real)
<type 'float'>
‘j’ indica la parte immaginaria
Operazioni su dati numerici
In Python le operazioni sui dati numerici sono gestite dai seguenti operatori:
• Operatori Unari: -,+,~
• Operatori Binari: -,+,*,/,%,**
• Operatori Logici (solo su int e long int): and, or, xor
• Operatori bit a bit (solo su int e long int): &, | ,^

Esistono inoltre funzioni built-in per lavorare con dati numerici, tra cui:
-abs(number)
-pow(x, y[, z])
-round(number[, ndigits])
-coerce

Nell’eseguire operazioni tra variabili numeriche di diverso tipo viene seguita


la seguente regola di conversione implicita:

IntLongFloatComplex
Operazioni su Dati Numerici

Esempio:
>>> k=5
>>> s=5+j
>>> type(s+k) #conversione a numero complesso
<type 'complex'>
>>> 4 and 2 # confronto logico
2
>>> 4 & 2
0 # confronto bit a bit tra i numeri binari 100 e 10
>>> 4 | 2
6 # confronto bit a bit tra i numeri binari 100 e 10
Operazioni su Dati Numerici

Il modulo math fornisce alcune delle più comuni funzioni matematiche.


Il modulo math non lavora su dati numerici complessi, per i quali esiste lo
specifico modulo cmath.

Le funzioni disponibili sono:


• Funzioni trigonometriche: cos, sin, tan, asin, acos, atan, sinh, cosh, tanh.
• Funzioni elevamento potenza e logaritmi: pow, exp, log, log10, sqrt
• Rappresentazione – trasformazione angoli ceil , floor, fabs, degrees,
radians.

Nel modulo math sono inoltre definite le costanti numeriche pi ed e.


Le stesse funzioni sono disponibili per i numeri complessi nel modulo cmath.
Operazioni su Dati Numerici

Per utilizzare il modulo math è sufficiente importarlo con la seguente sintassi:


import math
Oppure
from math import * (import all function)
from math import sin, cos (import specific function sinx, cosx)

>>>dir(math)
['__doc__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh', 'atan',
'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp',
'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot',
'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow',
'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']
Bool Type
E’ possibile utilizzare i valori interi per rappresentare valori booleani con la convenzione
che 0 corrisponda a FALSE e tutti i valori interi positivi corrispondano a TRUE.
E’ tuttavia buona norma di programmazione utilizzare il tipo bool per rappresentare
valori booleani. Una variabile di tipo bool può assumere valori TRUE e FALSE che
possono essere interscambiati con i valori 1 e 0
rispettivamente.
ESEMPIO
>>> a=1
>>> type(a)
<type 'int'>
>>> if(a):
print 'True'
True
>>> a=False
>>> type(a)
<type 'bool'>
Stringhe
• Una stringa letterale è una sequenza di caratteri racchiusa tra doppi o
singoli apici.

>>> a='ciao'
>>> b="Mondo"
>>> a+b
'ciaoMondo'
• Le sequenze di apici tripli “”” o ''' possono essere utilizzate per stringhe
che spaziano su più righe, o che contengono apici singoli o doppi (o tripli
dell'altro tipo):
Stringhe
Per accedere al singolo carattere si può ricorrere all’operatore [] oppure ad
una sottostringa con l’operatore [begin:end] (slicing)
Esempio
>>> a = “Hello world”
>>> a[1]
‘e’
>>> a[1:3]
‘el’
Non è possibile modificare il singolo carattere, ma è possibile assegnare alla
stringa un nuovo valore
Esempio
>>> a=‘Primo valore’
>>> a = “Change value” #Ok riassegnamento
>>> a[2] = ‘3’ #Errore
Manipolazione – Formattazione
Gli operatori + e * possono essere utilizzati per la manipolazione di stringhe.
La precedenza tra gli operatori viene mantenuta.

Esempio
>>> a = ‘Hello’
>>> a+a+a # Concatenazione
‘HelloHelloHello’
>>> a = ‘He’+’l’*2+’o World’ # Concatenazione e ripetizione
>>> a
‘Hello World’

Una stringa è una sequenza di caratteri: lettere, numeri, simboli e sequenze di


escape (o di controllo).
L’escaping permette di effettuare il quoting di un singolo carattere. Attraverso
l’escaping è possibile aggiungere apici o altri caratteri all’interno di una
stringa.
Manipolazione - Formattazione
Esempio
>>> a = ‘What’s your name?’ #Errore
SyntaxError: invalid syntax
>>> a = “What’s your name?” # Ok con doppio apice
>>> a = ‘What\’s your name?’ # Ok con escape sentence

Altri caratteri frequentemente utilizzati nella gestione delle stringhe sono:


• \t Tab ‘Ciao\tciao!’  Ciao ciao!
• \n New Line ‘Ciao\nciao!’  Ciao
ciao!
• \\ Backslash ‘c:\\Programmi\\pp’  c:\Programmi\pp
• \” Doppio apice ‘Repeat: \”Hello\”’  Repeat: ”Hello”
• \’ Apice “Repeat:\’Hello\’” Repeat: ‘Hello’
• \b Backspace “Hello \b World” HelloWorld
Manipolazione - Formattazione
Una stringa letterale preceduta da r o R viene detta Raw String: un carattere
preceduto dal backslash viene incluso nella stringa senza cambiamenti.
Esempio
>>> a = r’Hello \t World’ #Raw string
>>> a
‘Hello \t World’

Anche in python è possibile formattare l’output. Il carattere % ha un


significato speciale se usato con le stringhe.
Esempio

>>> ”Oggi è %s %d %s” % (“Venerdì’’,20,”Febbraio”)


>>>print _
Oggi è Venerdì 20 Febbraio
Per la formattazione si possono utilizzare anche: %f, %c, %x, %o, %u, %%, %e.
Metodi Built-in
Le stringhe sono, come ogni entità in Python, oggetti e dispongono di una
serie di funzionalità accessibili tramite dei metodi built-in.
• Manipolazione: concatenazioni, split, eliminazione caratteri e unioni.
-split([sep [,maxsplit]])
-replace (old, new[, count])
-strip([chars])
Esempio
>>> s='Ciao Mondo'
>>> s.split('o',1)
['Cia', ' Mondo‘]
>>> s.replace('o','i',1)
'Ciai Mondo‘
s.strip('C')
'iao Mondo '
Metodi Built-in
• Formattazione: allineamento, maiuscole, minuscole
-center(width[, fillchar]) e ljust(width[, fillchar]) e rjust(width[,fillchar])
-upper() e lower() e swapcase()
Esempio
>>> s = ‘Hello’
>>> s.center(10,’.’)
‘..Hello…’
>>> s.upper()
‘HELLO’
• Ricerca: ricerca e interrogazione sulla stringa
-find(sub [,start [,end]]) index(sub [,start [,end]])
-rindex(sub [,start [,end]]) e rfind(sub [,start [,end]])
-count(sub[, start[, end]])
-isupper() e islower()
-startswith(prefix[, start[, end]]) e endswith(prefix[, start[, end]])
Metodi Built-in
Esempio
>>> s = 'Hello World'
>>> s.count('o ',0,5)
1
>>> s.rfind('k')
-1
>>> s.rindex('k')
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
s.rindex('k')
ValueError: substring not found

NOTA: Oltre ai metodi built-in esiste il modulo string per la manipolazione di stringhe.
Alcune funzioni sono però già implementate come metodi nella classe str.
Confronti tra Stringhe
• Operatori logici : and, or, not
Una stringa vuota è valutata False mentre qualsiasi stringa
non vuota è valutata True.

Le funzioni min, max e cmp possono essere utilizzate anche sulle


stringhe.
Le stringhe sono inoltre sono dotate dell’operatore in e not in.

Esempio
>>> a = ‘HelloWorld’
>>> min(a), max(a)
(‘H’,’o’)
>>>’k’ in a
False
Conversioni tra Tipi
La conversione tra tipi nativi viene eseguita attraverso specifiche funzioni.
• Conversione da tipo stringa  a tipo numerico :
- int(), float(), complex(), long()
- ord()
- eval()
• Conversione da tipo numerico  a stringa:
- chr()
- str()
Esempio
>>> a=97.6
>>> print ord(chr(int(a)))
97
>>> b=5+3j
>>> _+b
(102+3j)
Esempio
Esempio
from math import *
g=9.81 #m/s
v0=15 #km/h
theta=60 #degree
x=0.5 #m
y0=1 #m
print """
Dati iniziali
v0=%f km/h
theta=%d gradi
y0=%f m
x=%f m
"""%(v0,theta, y0,x)
Esempio
#conversione dati
v0=v0/3.6
thetha=degrees(thetha)
y=x*tan(theta) -1/(2*v0**2)*g*x**2/(cos(thetha)**2) + y0
print “Posizione finale = “, str(y), ‘m’

OUTPUT:
Dati
v0=15.000000
theta=60
x=0.500000
y0=1.000000
Posizione finale = 1.16002019469
Polimorfismo
Il polimorfismo garantisce caratteristiche di concisione e di
flessibilità del linguaggio.
Essendo Python un linguaggio interpretato e con typing
dinamico, il polimorfismo è intrinseco nel linguaggio.
Esempio:
def somma(a,b): OUTPUT
7
return a+b Ciao mondo
[1,2,3,3]
print somma(3,4)
print somma(‘ciao’,’ mondo’)
print somma([1,2],[3,3])
Il polimorfismo garantisce un’unica interfaccia per operare su
tipi di dati differenti.
Costrutti
Control Flow
Con control flow (o strutture di controllo) si intende l’insieme dei costrutti che
servono a gestire il flusso di esecuzione di un programma, cioè a
specificare quando e come eseguire gli statements del codice.

Nei più diffusi linguaggi di programmazione i costrutti variano ma possono


essere classificati macroscopicamente in:
• Costrutti condizionali, l’esecuzione dipende dal raggiungimento di certe
condizioni.
• Costrutti iterativi, gli statements vengono eseguiti zero o più volte.
• Costrutti non locali, gestione delle eccezioni.
• Costrutti fondamentali, funzioni, metodi.

Python dispone dei più comuni costrutti di controllo. L’indentazione del


codice fornisce l’accorpamento dei comandi in blocchi di statements, al
posto delle parentesi usate nei più tradizionali linguaggi di
programmazione, p.e. C/C++.
If Statement
Il costrutto condizionale if ha la seguente sintassi:

if condition1:
block 1
elif condition2:
block 2

elif conditionN:
block N
else:
block statements

If è un costrutto condizionale: se la condizione è soddisfatta


viene eseguito il blocco di istruzioni corrispondente altrimenti
si analizzano le condizioni di verità dei blocchi elif.
If Statement
Il blocco if può avere più blocchi elif.
elif è la versione abbreviata di else if. Il costrutto elif è opzionale.
else corrisponde al caso di default, come elif è opzionale.
In un if statement al più viene eseguito un unico blocco (suite) di
istruzioni.
Esempio
>>> x=2000
>>> if x<10:
print ‘Piccolo’
elif x<100:
print ‘Medio’
else:
print ‘Grande’
‘Grande’
If Statement
L’espressione condizionale che deve essere verificata utilizza i comuni operatori logici
che il linguaggio mette a disposizione:
– Comparazione: <, <= , >, >= , == , !=
– Identità: is , is not
– Membership: in , not in
– Logical: not, and, or
Esempio
if answer==‘copy’:
copyfile=‘file.tmp’
elif answer==‘run’ or answer==‘execute’:
run=True
elif answer==‘quit’ :
quit=True
else:
print “No Valid Answer “
Switch Case in Python
Il costrutto switch-case permette di controllare il flusso del programma in
base al valore di una variabile o espressione.
Python non dispone del costrutto condizionale switch, la gestione di
espressioni condizionali con N sottocasi viene gestita con il costrutto if-
elif-else.
Python ha un’alternativa più semplice ed espressiva per il costrutto
switch-case presente in altri linguaggi di programmazione.
In molti linguaggi come in C/C++ il costrutto switch –case può gestire solo
dei valori interi. La soluzione implementata in Python permette di
gestire il costrutto case in modo generale.

Il funzionamento del costrutto switch-case può essere emulato utilizzando


opportunamente gli array associativi – dictionary.
{key1: f1, key2: f2,…keyN: fN}
For Statement
Il costrutto for è un costrutto iterativo che viene utilizzato per ripetere una suite di
istruzioni un numero definito di volte.
La sintassi:
for <variable> in <sequence>:
block
Python può eseguire dei loop su qualsiasi tipo di sequenza di dati: tuple, liste,
stringhe.
Esempio
>>> s = ‘Ciao’
>>> for i in s:
print s
C
i
a
o
For Statement
Per iterare su una sequenza di interi, Python dispone della funzione built-in
range([start,] stop[, step]) che restituisce una lista di interi .
Per ottimizzare l’efficienza del codice nei loop è preferibile utilizzare la
funzione xrange([start,] stop[, step]).
Il vantaggio di xrange risiede nell’utilizzo della memoria: xrange richiede
sempre la stessa quantità di memoria indipendentemente dalla
dimensione del range di valori da rappresentare.
Esempio
x=100000000 x=100000000
t1=time.clock() t1=time.clock()
for el in range(x): for el in xrange(x):
pass VS pass
t2=time.clock() t2=time.clock()
print “Time ", t2-t1, "s“ print “Time ”,t2-t1, “s”
Time 6.66 s Time 1.9 s
While Statement
Il costrutto while permette di eseguire un blocco di istruzioni un numero
indefinito di volte finché una particolare espressione risulta True.
La sintassi generale:
while (condition):
block
L’espressione condition viene valutata ad ogni ciclo e il ciclo viene ripetuto
finchè condition==True

In Python non esiste il costrutto do-while. Il ciclo while viene eseguito


almeno una volta se la condizione di partenza risulta True.
While Statement
Esempio
import random
number=random.randint(0,10)
guess=False
while(not guess):
s=input('Inserisci un intero: ')
if s==number:
print 'Complimenti! Numero segreto indovinato'
guess=True
elif s<number:
print 'Numero deve essere più grande'
guess=False
elif s>number:
print 'Numero deve essere più piccolo'
guess=True
Continue - Break
L’esecuzione di un loop può essere interrotta qualora siano verificate condizioni
eccezionali.
L’istruzione break interrompe l’esecuzione del loop. Per i cicli annidiati, break
interrompe esclusivamente l’esecuzione del loop più interno.
L’istruzione continue salta all’iterazione successiva di un ciclo. Nel caso di cicli
annidiati, continue agisce sul ciclo più interno.
for <variable> in >iterable: while(condition):
statements1 statements1
for <variable2> in <iterable>: if(condition):
if (condition2): continue
continue elif(condition2):
elif (condition): break
break statements2
statements2
Continue - Break
Esempio- break: calcolo dei numeri primi fino ad n
n=raw_input('Inserisci un numero intero ')
n=int(n)
for k in range(3,n+1):
prime=False
print ‘Current Number', k
for i in range(2,k):
print ‘Divided by', i
if k%i == 0:
prime=False
break
prime=True
if prime:
print 'Numero primo:', k
Continue - Break
Output
Inserisci un numero intero 6
Current number 3
Divided by 2
Numero primo: 3

Current number 4
Divided by 2

Current number 6
Divided by 2

Current number 5
Divided by 2
Divided by 3
Divided by 4
Numero primo: 5
Continue - Break
Esempio: calcolo della distanza minima tra un punto p1 e un insieme di punti nel
primo quadrante
from math import *
from pylab import*
import sys
lista=[(2,3),(4,5),(1,7),(-1,6),(-2,-4),(2,-8),(1,1)]
p1=input('Inserisci la coordinata lungo x: ')
p2=input('Inserisci la coordinata lungo y: ' )
point=(p1,p2)
c_point=()
dist_min=0
cur_dist=-1
dist=sys.float_info.max
Continue - Break
x=[]
y=[]
for k in range(len(lista)):
x.append(lista[k][0])
y.append(lista[k][1]) Jump
Escludiamo i punti fuori
if lista[k][0]<0 or lista[k][1]< 0:
dal primo quadrante
continue
cur_dist=sqrt((lista[k][0]-point[0])**2+(lista[k][1]-point[1])**2)
if cur_dist==0:
print 'Punti coincidenti'
break
if cur_dist<dist:
Break
dist=cur_dist
c_point=lista[k]
Continue - Break

print 'Distanza minima : ', dist


print 'Punto di minimo: ', c_point

plot(x,y,'o',[point[0]],[point[1]],'ro')

#OUTPUT
Inserisci la coordinata lungo x: 1
Inserisci la coordinata lungo y: 2
Distanza minima : 1.0
Punto di minimo: (1, 1)
Pass

• Pass è un istruizione generica che ‘non fa niente’. Può essere


inserita in qualsiasi parte del codice.
• E’ utile in situazioni in cui è necessario uno statement per
ragioni sintattiche.
• Esempio il corpo di una funzione non può essere omesso.

>>> def f(a,b):


pass

>>>a=3
>>>b=4
>>>f(a,b)
For While & Else Clause
Ai cicli condizionali for e while può essere associato un else statement, con la
seguente sintassi:
for <variable> in <sequence>: while(condition1):
block1 block1
else: else:
block2 block2

Il blocco else viene eseguito al termine dell’esecuzione dei rispettivi cicli a


patto che questi non siano terminati con l’istruzione break.

Esempio
n2=1000
step=5
print “Number n1 is in range ( %d , %d) with step %d?”%(0,n2,step)
number=int(raw_input(“Insert number n1: “))
For While & Else Clause
for i in range(0,n2,step):
if i==n1
print ‘n1 IS in range’
break
else:
print ‘n1 IS NOT in range’

#OUTPUT:
Number n1 is in range ( 0 , 1000) with step 5
Insert n1: 77
n1 IS NOT in range
Insert n1: 55
n1 IS in range
Gestione delle Eccezioni
La gestione delle eccezioni è uno strumento che permette di gestire errori che
tipicamente si verificano durante l’esecuzione di un programma.
Questo strumento permette di aumentare la robustezza di un programma e
prevenire crash che comprometterebbero la normale esecuzione del codice.
Ci sono almeno due tipologie di errore: errori sintattici ed eccezioni, generati
quando la sintassi è corretta ma viene prodotto un errore a run-time.
Esempio:
>>> for i in range(10) print ‘Ciao’
SyntaxError: invalid syntax
>>> a = 10/0
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
a=10/0
ZeroDivisionError: integer division or modulo by zero
Try - Except
Il costrutto try-except permette di rilevare le eccezioni e passare il controllo di flusso
ad un blocco preposto: exception handler, con la seguente sintassi:
try :
block1
except:
block2
In questo modo viene eseguito il blocco try e se viene generato un errore l’esecuzione
prosegue nel blocco except. Qualsiasi errore viene gestito nel blocco except a
meno di specificare più blocchi except ognuno dedicato alla gestione di una
particolare tipologia di errori.

Esempio:
>>>s=input(‘Inserisci un intero: ’)
>>>a=s/10 #Ma se s=0 o s=stringa?
Try - Except
#Soluzione
query=True
while(query):
try:
s=input('Inserisci un numero: ')
a=10.0/s
print a
query=False
except NameError:
print 'Inserisci un numero'
query=True
except ZeroDivisionError:
print 'Divisione per zero!‘
query=True
Try - Except
Un except clause può gestire più tipologie di eccezioni, che vengono
specificate attraverso una tupla di valori:
except (exception1, exception2,…,exceptionN)
Il costrutto try-except può avere un else-clause opzionale.
try:
block1
except e1:
block2

except eN:
blockN
else:
blockN+1
L’ else-clause viene eseguito quando il costrutto try non genera eccezioni.
Try - Except
Il blocco except ammette anche la sintassi:
except ExceptionName,e :
Le informazioni aggiuntive riguardo l’oggetto che hanno causato
l’eccezione vengono salvate in e.
Esempio:
try:
s=input(“Inserisci un numero: “)
a=10.0/s
except NameError,e:
print “Errore “, e

Inserisci un intero: stringa


no name ‘stringa' is not defined
Try - Except
L’ordine con il quale appaiono gli handlers delle eccezioni, all’interno di un costrutto
try-except, è rilevante: dalla più specifica alla meno specifica.

NOTA: Non è necessario importare esplicitamente il modulo exception.


Oltre alle eccezioni built-in è possibile per l’utente creare nuove eccezioni.
Il meccanismo di generazioni di eccezioni user-defined è legato alla programmazioni
ad oggetti: qualsiasi nuova eccezione deve ereditare dalla classe base Exception.
Raise Exception
E’ possibile forzare la generazione di un’eccezione attraverso lo statement raise.
Lo statement raise ha la seguente sintassi:
raise ExceptionName,Argument
Il primo argomento indica il nome dell’eccezione built-in o user-defined.
Esempio :
try:
a,b,c=input(‘a,b,c: ‘)
if a==0:
raise Exception, 'Impossibile, a=0‘
if b*b-4*a*c<0:
raise Exception, 'b^2-4ac <0, negativo, non ci sono radici reali‘
x1= (-b+math.sqrt(b*b-4*a*c))/(2*a)
x2= (-b-math.sqrt(b*b-4*a*c))/(2*a)
print "Le radici sono", x1, x2
Raise Exception

except Exception,e:
print e
#OUTPUT:
a,b,c: 2,1,0
Le radici sono 0.0 -0.5
a,b,c: 0,2,1
Impossibile, a=0
Raise Exception
Esempio: raise-else clause
#Verificare se un elemento esiste in una matrice
found=0
for x in range(len(mat)):
for y in range(len(mat)):
k+=1
if mat[x][y]==s:
found=1
break
if found:
break
if found==1:
print 'Trovato alla '+str(k)+'-esima iterazione'
else:
print 'Non Trovato‘
Raise Exception
#Oppure
try:
for x in range(len(mat)):
for y in range(len(mat)):
k+=1
if mat[x][y]==s:
msg='Trovato alla‘ +str(k)+ '-esima iterazione'
raise Exception, msg
except Exception,msg:
print msg
else:
print 'Non trovato'
Finally Statement
Il costrutto try-except può essere integrato con lo statement opzionale finally.
Il costrutto finally serve a racchiudere un blocco di istruzioni che devono
essere eseguite in ogni caso, sia che venga o non venga generata
un’eccezione.
Se in un blocco try viene generata un’ eccezione non gestita da un exception
handler, l’eccezione viene rilanciata dopo la finally clause.
Esempio:
a=raw_input('Inserisci un numero: ')
b=raw_input('Inserisci un secondo numero: ')
try:
d=int(a)/int(b)
except ZeroDivisionError,e:
print 'Divisione per zero:', e
Finally Statement
else:
print 'Divisione: ',d

finally:
print 'Esecuzione di finally‘
#OUTPUT:
Inserisci un numero: 5
Inserisci un secondo numero: 0
Divisione per zero: integer division or modulo by zero
Esecuzione di finally

Nella pratica è utile l’utilizzo dello statement finally per controllare il rilascio
di risorse esterne p.e. chiudere un file, una connessione di rete, chiudere
una connessione con un database, ect. anche se il codice è andato in
crash.
NOTA:
Il costrutto try-except-finally è supportato a partire dalle versioni 2.5.x
Dato Strutturato
Indice
• Tipi di dato strutturato
• Liste
• Tuple
• Dictionary
• Switch Case e Dictionary
• Set/Frozen Set
• Sequenze dati e cicli
• Iteratori
• range e xrange
• shallow & deep copying
Tipo di Dato Strutturato
Oltre ai tipi di dato nativi del linguaggio come stringhe, interi, numeri in
virgola mobile o complessi, comuni anche ad altri linguaggi di
programmazione, Python fornisce delle strutture dati native del
linguaggio.
Tali strutture si suddividono in sequenze ordinate di dati o collezioni di dati.
Ma si possono anche classificare come strutture mutevoli o immutevoli.
Le built-in data structures in Python sono:

•Liste
•Tuple
•Dizionari
•Set
•Frozenset
Liste
list definisce un tipo di dato strutturato lista. Gli elementi che costituiscono
una lista possono essere istanze di oggetti nativi del linguaggio o istanze di
oggetti definiti dall’utente. Una lista può contenere contemporaneamente
elementi di diverso tipo.
Per istanziare una lista non è necessario specificare lunghezza o tipo di dato.
Gli elementi di una lista sono contenuti tra [].
Per accedere al singolo elemento della lista si ricorre all’operatore [].
Esempio:
>>>mylist=[]
>>>mylist=[‘Lista’,’di’,4,’elementi’]
>>>print mylis[2],mylist[0]
>>>4
L’ordinamento viene mantenuto. Una lista non è una collezione di dati ma è
una sequenza ordinata di dati.
Liste
• Le liste supportano l’operatore di slicing [start:stop:step]
>>> mylist =[0,1,2,3,4,5,6,7]
>>> mylist[0:6]
[0,1,2,3,4,5]
>>> mylist[1:6:2]
[1,3,5]
>>> mylist[1::2]
[1,3,5,7]
>>> mylist[::2]
[0,2,4,6]

Lo slicing può essere anche negativo


>>> mylist[6:0:-2]
[6,4,2]
Liste
• La funzione range() serve per generare liste di numeri interi
• La funzione range(start,stop,step) genera una lista di interi da start a stop con
passo step.

>>> mylist =range(3)


[0,1,2]
>>>type(mylist)
<type 'list'>
>>> mylist =range(1,10)
[0,1,2,3,4,5,6,7,8,9]
>>> mylist =range(1,10,2)
[1,3,5,7,9]
Liste
• E’ comune generare nuove liste, dove ogni elemento è il
risultato di alcune operazioni applicate a ciascun elemento di
un'altra sequenza di dati, o è creato da una successione di
elementi che soddisfano una determinata condizione. La List
comprehension permette di definire delle liste ‘in stile
matematico’.
S = {x² : x in {0 ... 9}}
V = {2^x: x in {0,1,…,12}}
M = {x | x in S and x even}

>>>S=[x**2 for x in range (10)]


>>>V=[2**x for x in range(12)]
>>>M=[x for x in S if x%2==0]
Liste
• La list comprehension consiste in una coppia di [] contenenti un’espressione seguita da un
un for e da uno o più cicli for e costrutti if.
>>> mylist =range(10)
>>> mylistNew=[el*2 for el in mylist]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> l=[1,2]
>>> l2=['a','b']
>>> l3=[4,5]
>>> f=[(e1,e2,e3) for e1 in l for e2 in l2 for e3 in l3]
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), (2, 'a', 5), (2, 'b', 4), (2, 'b', 5)]
Equivalente a :
>>> for e1 in l:
for e2 in l2:
for e3 in l3:
f.append((e1,e2,e3))
Liste
Example: for and if combined
L=[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]

Che è equivalente a :

>>> combs = []
>>> for x in [1,2,3]:
for y in [3,1,4]:
if x != y:
combs.append((x, y))

Example: nested list comprehension


>>> matrix =
[ ... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12], ... ]
Matrix2=[[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Liste
Le liste sono contenitori dati modificabili. Gli oggetti di tipo lista contengono metodi
built_in per manipolare e modificare i dati membro.
• Inserimento
- append(object) inserimento di object in coda
- insert(index,object) inserimento di object in posizione index
- extend(iterable) concatenazione di liste

• Ricerca
-index(value, [start, [stop]]) ricerca di value e ritorno della prima
occorrenza
-count(value) calcolo delle occorrenze di value

• Eliminazione
-remove(value) eliminazione della prima occorrenza di
value
-pop([index]) eliminazione elemento in posizione index
Liste
• Ordinamento
-reverse() inversione dell’ordine degli elementi
-sort(cmp=None, key=None, reverse=False) ordinamento

NOTA:
Le liste sono una struttura dati flessibile e consentono
l’immagazzinamento di dati eterogenei, a costo di più memoria.
Lavorando con dati di un solo tipo gli array (modulo numpy) sono più
performanti.
Liste
Gli operatori + e * possono essere applicati anche sulle liste.
L’operatore + effettua una concatenazione tra due liste.
L’operatore * effettua una ripetizione.
Python supporta per le liste anche l’operatore += .
L’operatore + e la funzione extend hanno le stesse funzionalità. I
tempi di esecuzione sono però differenti.

Esempio:
import time
mylist =range(100000000)
mylist2 =range(1000000)
Liste
T1=time.clock()
s= mylist + mylist2
T2=time.clock()
print “ Tempo di esecuzione di + :’’ , T2-T1 , “s”
T3=time.clock()
mylist.extend(mylist2)
T4=time.clock()
print “ Tempo di esecuzione di extend :’’, T4-T3 , “s”

OUTPUT:
Tempo di esecuzione di +: 2.81 s
Tempo di esecuzione di extend: 0.033 s
Liste
I metodi implementati nella lista la rendono particolarmente adatta ad essere
utilizzata come stack o come queue.
I metodi pop e append possono essere usati per implementare la logica LIFO tipica
degli stack.
I metodi pop con indice 0 e append possono essere usati per implementare la logica
FIFO tipica della queue.

Esempio:
# file stack_queue.py
stack=[1, 2, 3, 4]
print ‘Stack iniziale:’, stack
for i in range(5,7):
stack.append(i)
print “Append: “, stack
stack.pop()
print “Pop:” , stack
Liste
queue=[ ‘a’,’b’,’c’,’d’ ]
print “Queue iniziale:”, queue
queue.append(‘e’)
queue.append(‘f’)
print “Append:”, queue
queue.pop(0)
print “Pop:”, queue

OUTPUT:
Stack iniziale: [1, 2, 3, 4]
Append: [1, 2, 3, 4, 5, 6]
Pop: [1, 2, 3, 4, 5]
Queue iniziale: ['a', 'b', 'c', 'd']
Append: ['a', 'b', 'c', 'd', 'e', 'f']
Pop: ['b', 'c', 'd', 'e', 'f']
Tuple
Le tuple sono sequenze ordinate di dati racchiusi tra (). Le tuple sono liste molto
particolari i cui elementi sono immutabili.
L’accesso al singolo elemento avviene attraverso l’operatore [].
Lo slicing [start:end] è ammesso anche sulle tuple.
Esempio:
>>>mytuple=(1,2,3,4)
>>>el= mytuple[1]
>>>t2= mytuple[0:2]
>>>print el, t2
2 (1,2)
I dati contenuti in una tupla possono essere di tipo eterogeneo.
Le tuple sono oggetti immutabili. Non contengono pertanto metodi di:
• eliminazione
• inserimento
• ricerca
Tuple
Esempio:
>>> mytuple =(1,2,3,4,’ciao’,’mondo’,[2,3])
>>> mytuple[3]=‘jkjk’
Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
t1[1]=3
TypeError: 'tuple' object does not support item assignment

Sulle tuple sono definiti gli operatori in e not in.


Le tuple sono indicate per definire set di valori costanti. Per alcune operazioni
risultano più efficienti delle liste.
Tuple
Esempio:
import timeit
sum=0
t0=timeit.Timer('t=(1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,
3,4,5,6,7,8)')
t00=t0.timeit()
print ‘Tuple creating time ‘,t00, ' \n'

t1=timeit.Timer('l=[1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,
3,4,5,6,7,8]')
t11=t1.timeit()
print ‘List creating time ‘,t11, ' \n'

t=tuple( range(9000))
l=range(9000)
Tuple
OUTPUT:

Time creating a tuple 0.0415067049835


Time creating a list 1.07190083708
Time accessing a tuple 0.0719978947727
Time accessing a list 0.0729975422291

E’ possibile convertire un dato tupla in un dato lista e viceversa


rispettivamente attraverso le funzioni list e tuple.
Esempio:
>>>mylist=[1,2,1,2]
>>>mytuple=tuple(l)
>>>tupleToList=list(mytuple)
>>>type(mytuple); type(tupleToList)
<type 'tuple'>
<type ‘list'>
Dictionary
Un dictionary è un insieme non ordinato di oggetti. Ogni oggetto di un
dictionary è identificato univocamente da una key.
Ogni elemento di un dictionary è rappresentato da una coppia key – value.
In Python le keys possono essere solo oggetti immutabili: le tuple, per
esempio, possono essere usate come chiavi di indicizzazione di un
dictionary.
Come le tuple e le liste anche i dizionari sono tipi built-in.
I dati sono racchiusi tra { }, l’accesso al singolo elemento avviene
attraverso [ ].

Esempio:
>>>mydic={ } # dizionario vuoto
>>>mydic={1: ’Hello’, ‘due’: ’World’} # dizionario con due elementi
>>>mydic[1]
‘hello’
Dictionary
Le chiavi in Python sono univoche e sono inoltre case-sensitive.
I dati contenuti in un dizionario possono essere eterogenei.
Esempio:
>>>mydic={‘a’:’Ciao’,’b’:’Mondo’}
>>> mydic[a]=‘CIAO’;
>>> mydic
{1: 'CIAO', 'due': 'world'}
>>> mydic[‘c’] # Errore la chiave non è presente
Traceback (most recent call last):
File "<pyshell#73>", line 1, in <module>
mydic['c']
KeyError: 'c’
>>>(mydic.key(), mydic.value())
([1, 'due'], ['CIAO', 'world'])

Le funzioni key() and value() ritornano rispettivamente le chiavi e i valori di un


dizionario.
Dictionary
I dizionari vengono manipolati attraverso le funzioni di:
• Update:
Un dizionario può essere modificato aggiungendo items o modificando
gli esistenti. I metodi built-in preposti:
- update(E, **F)

Esempio:
>>> mydic ={1:’Hello’,2:’World’ }
>>> mydic.update({3:’Ciao’}) # aggiunta tramite il metodo update
>>> mydic[4]=‘Mondo’ # aggiunta di un item
>>> mydic[2]=‘world’ # modifica di un item
>>> mydic
{1: 'Hello', 2: 'World', 3: 'Ciao', 4: 'Mondo'}
Dictionary
• Eliminazione:
Tramite cancellazione di un elemento o eliminazione dell’intero contenuto di un
dizionario. I metodi preposti:
- clear()
- pop(k[,d])
- popitem()

Esempio:
>>> mydic ={1:’Mon’,2:’Tue’,3:’Wed’,4:’Thu’,5:’Fri’,6:’Sat’,7:’Sun’}
>>>item= mydic.popitem() # popitem
>>> mydic
{2: 'Tue', 3: 'Wed', 4: 'Thu', 5: 'Fri', 6: 'Sat', 7: 'Sun'}
>>>item= mydic.pop(3) # pop
{2: 'Tue', 4: 'Thu', 5: 'Fri', 6: 'Sat', 7: 'Sun'}
Dictionary
>>>del mydic[4] #del
>>> mydic
{2: 'Tue', 5: 'Fri', 6: 'Sat', 7: 'Sun'}
>>> mydic.clear() #clear
>>> mydic
{}
• Altri metodi:
-get(k[,d])
-has_key(k)
-items()
Esempio:
>>> mydic ={1:’Mon’,2:’Tue’,3:’Wed’,4:’Thu’,5:’Fri’,6:’Sat’,7:’Sun’}
>>> mydic.has_key(8)
False
Dictionary
>>> mydic.get(7)
'Sun‘
>>> mydic.items()
[(1, 'Mon'), (2, 'Tue'), (3, 'Wed'), (4, 'Thu'), (5, 'Fri'), (6, 'Sat'), (7, 'Sun')]

Un utilizzo interessante dei dizionari o array associativi consiste


nell’immagazzinamento di matrici sparse. Una struttura dati efficiente per
l’immagazzinamento di matrici sparse consente di ridurre i costi
computazionali e di ridurre l’occupazione in memoria.
Nel formato COordinate si immagazzinano le coppie (i,j) corrispondenti ai valori aij
non nulli.
Esempio: 1 0 0 
a  0 0 1 d  (0,0) : 1, (1,2) : 1, (2,0) : 2
2 0 0
Dictionary
Al posto delle tuple, si potrebbe usare come chiave un intero, secondo la
mappatura (per righe): (i,j)j+i*n dove n è il numero di colonne.

Nell’ esempio precedente:


Esempio:
#a è una lista di liste a=[[a1, a2,…an], …, [m1,m2,…,mn]]
#Matrice sparsa 100 X 100:
#Definizione del dizionario con tuple
mydic ={}
for i in range(0,len(a)):
for j in range (0,len(a[0])):
if a[i][j]>0:
t=(i,j)
mydic.update({t:a[i][j]})
Dictionary
#Definizione del prodotto matrice-vettore con il metodi COordinate
def prodMatVecDict(d,v,l):
res=[0 for el in xrange(len(v))]
for k in d.keys(): Ciclo solo sugli elementi non nulli, non su tutta
la matrice
i=k[0]
j=k[1]
val=d[k]
res[i]+=val*v[j]
#Definizione del prodotto matrice-vettore standard
def prodMatVecList(a,v):
res=[]
Dictionary
for i in range(0,len(a)):
somma=0.0
for j in range(0,len(a[0])):
somma+=a[i][j]*v[j]
res.append(somma)
#Tempi di calcolo:
Prodotto matrice vettore liste: 400.151556209 microsec
Prodotto matrice vettore dict: 17.8503196928 microsec
#Definizione moltiplicazione per uno scalare con array associativo
def prod_scalare_dict(d,s):
dd={}
for k in d.keys():
dd.update({k:d[k]*s})
Dictionary

#Definizione della moltiplicazione per uno scalare caso std


def prod_scalare_list(a,s):
for i in range (0, len(a)):
for j in range(0, len(a[0])):
a[i][j]=a[i][j]*s

#Tempi di Calcolo
Prodotto per uno scalare list: 467.861930815 microsec
Prodotto per uno scalare dict: 22.1639234568 microsec
Esempio

• Definite un dizionario che metta in relazione alcuni dei libri ch


e avete letto con il nome del relativo autore.
• Stampare i nomi di tutti gli autori.
• Stampare per ciascun autore il numero di libri presenti nel
dizionario
• Costruire una lista di tutti i libri il cui titolo non contiene il cara
ttere “s”.
Switch Case e Dictionary
Il costrutto switch-case permette di controllare il flusso del programma in base al
valore di una variabile o espressione.
In Python è possibile emulare il funzionamento del costrutto switch-case utilizzando
come struttura dati dei dictionary.
La forma generale è la seguente:
{
Key_1: function_1
Key_2: function_2
Key_3: function_3

Key_N: function_N
}
E’ possibile gestire case diversi da interi.
Lo switch-case costruito con i dizionari risulta più efficiente del blocco if-else.
Switch Case e Dictionary
#definizione del dizionario
caso=random.randint(1,30)
d={1:f_1, 2:f_2, 3:f_3, 4:f_4, 5:f_5, 8:f_1, 9:f_1,10:f_1,11:f_1
,12:f_2,13:f_2,14:f_3,15:f_3,16:f_4,17:f_4,18:f_5,19:f_5}.get(caso,default)()

#definizione del blocco if-elif-else


if caso==1 or caso==8 or caso==9 or caso==10 or caso==11:
f_1(y)
elif caso==2 or caso==12 or caso==13:
f_2(y)
elif caso==3 or caso==14 or caso==15:
f_3(y)
elif caso==4 or caso==16 or caso==17:
f_4(y)
Switch Case e Dictionary

elif caso==5 or caso==18 or caso==19:


f_5(y)
else:
default(y)

#OUTPUT

Tempo di calcolo utilizzando dict: 0.07 sec


Tempo di calcolo utilizzando if-elif: 1.46 sec
Set – Frozenset
Python mette a disposizione due distinte strutture dati per la rappresentazione di
insiemi di elementi.
Un set è una collezione mutabile non ordinata di oggetti.
Un frozenset è una collezione immutabile non ordinata di oggetti.
In entrambi i casi gli elementi sono univoci.
Esempio:
>>>s=set((‘ciao’,1,’Mondo’))
>>>fs=frozenset((‘ciao’,2))

I set mettono a disposizione dei metodi per la modifica dell’insieme di dati.


• Inserimento:
-add(obj)
-update(obj)
Set – Frozenset
• Eliminazione
-discard(x) remove(x)
-clear()
-pop()
Esempio:
>>>s=set(('abc','def',1,2,3,'ghi'))
>>>s.add(4)
>>>s.update((‘lmn’,5))
>>>s
set([1, 2, 3, 4, 5, 'abc', 'lmn', 'ghi', 'def'])
>>>s.discard(4)
>>>s.remove(5)
>>>s.pop()
Set - Frozenset
>>>s
set([2, 3, 'abc', 'lmn', 'ghi', 'def'])
>>>s.clear(); s
set([ ])
Entrambi i contenitori dati dispongono di metodi per gestire le operazioni tra
insiemi:
-union,
-intersection,
-difference,
-issubset,
-issuperset
Esempio:
>>>s=set((1,2))
>>>s2=frozenset((2,3,4))
Set - Frozenset
>>>s3=s.union(s2)
>>>s4=s.difference(s2)
>>>s5=s2.intersection(s)
>>>s.issubset(s2)
False
>>>print ’s3’, s3 , ’s4’, s4, ’s5’, s5
s3 set([1, 2, 3, 4]) s4 set([1]) s5 frozenset([])

In entrambi i casi i dati contenuti possono essere eterogenei.


Inoltre i frozenset in quanto immutabili possono essere utilizzati
per l’indicizzazione dei dizionari.
Esempio

• Avendo a disposizione solo queste strutture dati:


>>> all = set(range(1, 20))
>>> primes = set([2, 3, 5, 7, 11, 13, 17, 19])
>>> even = set([2, 4, 6, 8, 10, 12, 14, 16, 18])
• stampare l'insieme dei numeri dispari
• l'insieme dei numeri dispari primi,
• l'insieme dei numeri dispari non primi.
Riassumendo

Eterogenea Mutable Ordered


List [] V V V
Tuple () V V
Dict {} V V
Set V V
FrozenSet V
Sequenze dati e cicli
• Il ciclo for consente di iterare su oggetti iterabili come
lista,tuple,stringhe, set, dizionari.

LISTE STRINGHE SET


>>> a=[1,2,3,4,5] >>> a=‘’’Ciao’’’ >>> a=set([1,2,3,4])
>>> for el in a: >>> for el in a: >>> for el in a:
print el print el print el
1 C
2 i 1
3 a 2
4 o 3
5 4
Sequenze dati e cicli
DIZIONARI chiave DIZIONARI valori DIZIONARI chiave-valori

>>> a={1:’a’,2:’b’} >>> a={1:’a’,2:’b’} >>> a={1:’a’,2:’b’}


>>> for el in a.keys(): >>> for el in a.values(): >>> for k,v in a.items():
print el print el print k,v

1 a 1a
2 b 2b

DIZIONARI DIZIONARI
>>> a={1:’a’,2:’b’} >>> a={1:’a’,2:’b’}
>>> for el in a: >>> for el in (1,2,3):
print el print a.get(el)

1 a
2 b
None
Iteratori
Per ciclare sugli oggetti di un array (lista o tupla o stringa; in generale (containers))
si utilizza il ciclo for.
Il ciclo for per operare all’interno degli elementi del containers utilizza un
oggetto detto iteratore.
L’oggetto iteratore ha le caratteristiche necessarie per muoversi nel contenitore:
1. possiede una funzione (metodo next()) che restituisce il primo dato disponibile
nel contenitore
1. lancia automaticamente l'eccezione quando non ci sono più dati nel contenitore:
StopIteration

Infatti:
>>> a=[1,2,3]
>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__‘
, '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__',
'__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append‘
, 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Iteratori
>>> my_iter=a.__iter__()
>>> dir(my_iter)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__iter__', '__length_hint__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__', 'next']

>>>try:
while(1):
print my_iter.next()
except:
print 'Stop iteration‘
>>>1
>>>2
>>>3
>>>Stop iteration
usando questi oggetti è possibile implementare un ciclo for più performante soprattutto
a livello di memoria quando il dominio su cui si vuole ciclare è molto grande (xrange)
range e xrange

Come visto negli esempi del corso range restituisce una lista di
elementi su cui ciclare.

xrange invece sfrutta il concetto di iteratore di container per non


creare da principio tutta la lista di indici ma fornendo uno strumento
che a run-time genera l’indice desiderato.

Il risultato è un ciclo for più performante e decisamente meno costoso


in termini di memoria quando le dimensioni del ciclo sono grandi.
range e xrange
from memtracktest import memory
from time import sleep
#range
X=[100,1000,10000,100000,1000000,10000000]
for x in X:
m1=memory()
#range
a=range(x)
m2=memory()
print "Size of the cycle: ",x," Memory used by range: ",m2-m1," bytes"
#xrange
b= xrange(x)
m3=memory()
print "Size of the cycle: ",x," Memory used by xrange: ",m3-m2," bytes"
range e xrange
Size of the cycle: 100 Memory used by xrange: 4096.0 bytes
Size of the cycle: 100 Memory used by range: 0.0 bytes
Size of the cycle: 1000 Memory used by range: 8192.0 bytes
Size of the cycle: 1000 Memory used by xrange: 0.0 bytes
Size of the cycle: 10000 Memory used by range: 135168.0 bytes
Size of the cycle: 10000 Memory used by xrange: 0.0 bytes
Size of the cycle: 100000 Memory used by range: 1056768.0 bytes
Size of the cycle: 100000 Memory used by xrange: 0.0 bytes
Size of the cycle: 1000000 Memory used by range: 10887168.0 bytes
Size of the cycle: 1000000 Memory used by xrange: 0.0 bytes
Size of the cycle: 10000000 Memory used by range: 108879872.0 bytes
Size of the cycle: 10000000 Memory used by xrange: 0.0 bytes

Range aumenta linearmente con la dimensione del ciclo


(la lista viene effettivamente generata e salvata in memoria)
xrange ha un costo costante.
Shallow & Deep Copying
Con shallow copying si intende una copia per referenza.
Con deep copying si intende una copia per valore.
Python di default utilizza una shallow copy nella copia di oggetti mutabili e un deep
copy nella copia di oggetti immutabili.
Esempio:
>>>a=’Ciao’ # stringa è immutabile
>>>b=a
>>>b+=’ Mondo’
>>>a;b
’Ciao’
’Ciao mondo’
>>>lista=[1,2] # lista è mutabile
>>>b=lista
>>>b==lista
Shallow & Deep Copying
True
>>>b is lista
True
>>>b.append(3)
>>>b ; lista
[1,2,3]
[1,2,3]
>>>c=lista[ : ] # slicing opera per valore sulle liste
>>>c is lista ; c==lista
False
True
>>>c.append(5)
>>> c ; l
[1,2,5]
Shallow & Deep Copying
[1,2]
>>>lista=[1,2,[3,4]]
>>>d=lista[:]
>>>d[2].append(5)
>>>d; lista # NOT DEEP! Lo slicing agisce solo ad un
livello
[1,2,[3,4,5]]
[1,2,[3,4,5]]

Python dispone del modulo copy per controllare le operazioni di


assegnamento tramite deep e shallow copying.
Il modulo copy contiene due funzioni copy e deepcopy rispettivamente per la
shallow e la deep copy.
Shallow & Deep Copying
Esempio:
>>>from copy import *
>>>l=[1,2,[3,4]]
>>>d=copy.deepcopy(l)
>>>d.append(5)
>>>d; l
[1,2,[3,4,5]]
[1,2,[3,4]]

La funzione deepcopy agisce su ogni livello di profondità.


Funzioni
Funzioni: Definizione

Una funzione è un blocco organizzato di codice che viene


utilizzato per eseguire un preciso task.

L’utilizzo di funzioni garantisce maggiore modularità al codice e


una maggiore riutilizzabilità dello stesso.

Python dispone di funzioni built-in, p.e. print(), help(), dir() etc,


ma è ovviamente possibile definire nuove funzioni: user-
defined-function.
Funzioni: Definizione

La definizione di una funzione in python ha la seguente sintassi:

def NameFunction(arg1,arg2,…,argN):

La keyword def è seguita dal nome della funzione e dalla lista dei
suoi argomenti.
Al prototipo della funzione segue il corpo della definizione.
Funzioni: Definizione
E’ possibile aggiungere una stringa di documentazione antecedente il corpo
della funzione per spiegare l’utilizzo della funzione e i parametri:
docstrings.
Esempio:
def stampa(stringa): Prototipo della funzione
"""La funzione stampa stampa a video la stringa stringa """ Docstring
print stringa Corpo della funzione

help(stampa)
stampa(‘Ciao’) Chiamata della funzione
Output
Help on function stampa in module __main__:
stampa(stringa)
La funzione stampa stampa a video la stringa stringa.
Ciao
Funzioni: def statement
Lo statement def è uno statement esecutivo a tutti gli effetti.
Quando viene eseguito crea una nuova function object e le
assegna un nome.
In Python è perfettamente legale dichiarare una funzione
incapsulata in altri statements.

if test1: def void():


def f1(): def void2()

else: return something


def f2():
Funzioni: def statement

La funzione f1 non viene creata finchè lo statement def non


viene raggiunto ed eseguito: in Python tutto viene valutato a
runtime al contrario dei linguaggi compilati.

Esempio: dyn_def.py

Nota Nell’esecuzione dell’import di un modulo tutte le function


object in esso definite vengono create.
Argomenti di una Funzione
Passaggio di argomenti
In Python è importante distinguere tra oggetti mutabili e
immutabili per capire il passaggio di argomenti ad una
funzione.
Gli oggetti immutabili (stringhe, tuple, numeri) vengono passati
per valore: variazioni di questi parametri non hanno effetti sul
programma chiamante.
Gli oggetti mutabili (liste, dictionary) vengono passati per
referenza: variazioni di questi parametri si propagano al
programma chiamante.
Inoltre le variabili definite nel corpo di una funzione hanno scope
locale a quella funzione.
Argomenti di una Funzione
Esempio
lista=[1,2,3,3,4,4,7,8]
def remove_duplicate(vect):
print “Vect inside function, before: ”, vect
for i in vect:
c=vect.count(i)
if c>1:
vect.remove(i)
print “Vect inside function after: ”, vect
remove_duplicate(lista)
print “Vect outside function ”,lista
Argomenti di una Funzione
Output
Vect inside function, before: [1,2,3,3,4,4,7,8]
Vect inside function after: [1,2,3,4,7,8]
Vect outside function: [1,2,3,4,7,8]
Argomenti di una Funzione
Esempio2
lista=[1,2,3,3,4,4,7,8]
def change_vect(vect):
print “Vect inside function, before: ”, vect
vect[2]=-99; vect=[4,5,6,6,7]
print “Vect inside function, after: ”, vect
change_vect(lista)
print “Vect outside function, after:”, lista
Output
Vect inside function, before: [1,2,3,3,4,4,7,8]
Vect inside function after: [4,5,6,6,7]
Vect outside function: [1,2,-99,3,4,4,7,8]
Argomenti di una Funzione
Argomenti di una funzione

Il numero di parametri (required parameters) passati ad una


funzione deve essere conforme al prototipo della funzione.
E’ possibile definire degli argomenti di default per una funzione,
associando al nome del parametro un valore di default.
I parametri di default seguono quelli obbligatori. Specificando
dei parametri di default la chiamata a funzione può essere
eseguita specificando meno parametri di quelli presenti nel
prototipo.
Argomenti di una Funzione
E’ possibile chiamare una funzione utilizzando dei keyword
arguments della forma keyword=value.
L’utilizzo dei keyword arguments permette di specificare i
parametri senza seguire l’ordine posizionale.

Esempio ordina.py
def insertion_sort(a):
for i in range(1,len(a)):
val=a[i]
j=i-1
while (j>=0 and a[j]>val):
a[j+1]=a[j]
j=j-1
a[j+1]=val
return a
Argomenti di una Funzione
def bubble_sort(a):
n=len(a)
while(n>0):
for i in range(0,n-1):
if(a[i]>a[i+1]):
tmp=a[i]
a[i]=a[i+1]
a[i+1]=tmp
n=n-1
return a
Argomenti di una Funzione
def ordina(a,method='bubble',copy=False):
if method=='bubble':
if copy==False:
b=bubble_sort(a)
else:
b=bubble_sort(a[:])
elif method==‘insert’:
if copy==False:
b=insertion_sort(a)
else:
b=insertion_sort(a[:])
else:
print “No valid method”
return b
Argomenti di una Funzione
v=[5,8,2,4,6,7,2,7,1,9,8]; print "v=",v
l=ordina(v,copy=True); print "l=",l; print "v=",v
l2=ordina(v); print "l2=",l2; print "v=",v
l3=ordina(v,copy=True,method=‘insert’); print "l3=",l3

Output:

v= [5, 8, 2, 4, 6, 7, 2, 7, 1, 9, 8]
l= [1, 2, 2, 4, 5, 6, 7, 7, 8, 8, 9]
v= [5, 8, 2, 4, 6, 7, 2, 7, 1, 9, 8]
l2= [1, 2, 2, 4, 5, 6, 7, 7, 8, 8, 9]
v= [1, 2, 2, 4, 5, 6, 7, 7, 8, 8, 9]
l3= [1, 2, 2, 4, 5, 6, 7, 7, 8, 8, 9]
Argomenti di una Funzione
Python consente di utilizzare come parametro di funzione una
lista di lunghezza variabile di argomenti. Un asterisco *
preposto al nome del parametro indica una lista di argomenti
di lunghezza variabile.

def func_var_arg(a,b,c,…,*args):

Questi argomenti vengono mantenuti in una tupla.


Argomenti di una Funzione
Esempio
def min_max_average(*args):
avg =0
n=0
for el in args:
n+=1
avg+=el
avg/=float(n)
min_e=min(args)
max_e=max(args)
print "Media ", avg
print "Min ", min_e
print "Max ", max_e
Argomenti di una Funzione
Ouput
>>min_max_average(3,2,1)
Media 2.0
Min 1
Max 3

>>min_max_average(2)
Media 2.0
Min 2
Max 2
Argomenti di una Funzione

Un doppio asterisco preposto al nome del parametro indica una


lista di lunghezza variabile di keyword arguments.

def func_var_arg(a,b,c,…,**args):

Questi argomenti vengono mantenuti in un dictionary.


Argomenti di una Funzione
Esempio
def function(a,**kw):
print a
print kw

Output
function(3)
3
{}
>>function(3,b=2,c=4)
3
{‘c’:4,’b’:2}
Argomenti di una Funzione
Python permette di utilizzare una funzione come argomento di
un’altra funzione. Questa situazione è abbastanza tipica. Data
una funzione f(x) è necessaria una funzione per:
• Calcolare gli zeri di f(x)
• Calcolare l’integrale di f(x) in [a,b]
• Calcolare un’approssimazione della derivata prima
• Etc

La funzione argomento viene trattata allo stesso modo di


qualsiasi altro argomento: un riferimento ad un object, in
questo caso ad un function-object.
Argomenti di una Funzione
Esempio Approssimazione della derivata seconda in un punto.
def f(x):
return x**3

def diff2(f,x,h=1e-6):
r=(f(x-h)-2*f(x)+f(x+h))/float(h*h)
return r

if __name__== ‘__main__’:
print “Diff2 di x^3 per x=“, 3, “ ”, diff2(f,3)
print “Diff2 di sin(x) per x=“, pi,” “, diff2(sin,pi)
Output
Diff2 di x^3 per x=3 18.001600210482138
Diff2 di sin(x) per x=pi 0.0
Argomenti di una funzione
Le funzioni in Python sono strutture molto flessibili: sono oggetti a
tutti gli effetti. Questo consente a una funzione di essere
argomento di un’altra funzione, return value, di essere assegnata ad
una variabile etc.
Esempio: Output
def f1(arg):
a=f1
print “Sono la funzione 1”
k=a(2)
if (arg): Sono la funzione 1
def f2(x):
print “Sono la funzione 2” xx=k(3)
Sono la funzione 2
print “x-value”, x x-value 3
return x
return f2 print “xx value “, xx
xx value 3
else: return None
Return Value
La keyword return permette di specificare dei valori di
ritorno di una funzione al programma chiamante.
In Python è possibile specificare più parametri di
ritorno.
Output:
Esempio: m=2, n=3
print “Prima m=“,m,“ n=“,n
def swap(a,b): Prima m=2 n=3
tmp=b swap(m,n)
b=a print “Dopo m=“,m,“ n=“,n
a=tmp Dopo m=2 n=3
Return Value

Esempio: Output:
def swap(a,b): m=2, n=3
tmp=b print “Prima m=“,m,“ n=“,n
Prima m=2 n=3
b=a
a=tmp m,n=swap(m,n)
return a,b print “Dopo m=“,m,“n=“,n
Dopo m=3 n=2
Scoping di Variabili

Con scope di variabili intendiamo la visibilità di una


variabile all’interno di un codice.
Con namespace si intende una collezione di nomi di
entità. In Python viene implementato tramite un
dizionario che associa ad ogni variabile il suo valore.
Ciascuna funzione definisce un proprio namespace
locale. Anche i metodi di una classe seguono le
regole di scoping delle normali funzioni.
Ciascun modulo definisce un proprio namespace.
Scoping di Variabili

Possiamo distinguere tra differenti namespace:


- Built-in namespace
- Module namespace definito in un modulo o in un file
- Local namespace, definito da una funzione o da una
classe
L’interprete durante l’esecuzione ricerca i nomi
nell’ordine:
Local namespace  Global namespace  Built-in namespace
Scoping di Variabili
Esempio:
Built-in
x=int(raw_input("Inserisci un numero: "))
Module/global

def square(x): Module/global


x=x*x Local
print “Inside function: x è uguale a ",x
square(x)
print “Outside function: x è uguale a “, x

Output:
Inserisci un numero: 5
Inside function: x è uguale a 25
Outside function: x è uguale a 5
Scoping di Variabili
ESEMPIO
>>>val=4
>>> if (val>5):
def func3(stringa):
print 'Sono la funzione func3',stringa+str(3)
else:
def func1(stringa):
def func2(stringa):
print 'Sono la funzione func2',stringa+str(2)
print 'Sono la funzione func1', stringa+str(1)
func2(stringa)
Scoping di variabile
>>> func3()
Traceback (most recent call last):
File "<pyshell#195>", line 1, in <module>
func3()
NameError: name 'func3' is not defined
>>> func1('Ciao')
Sono la funzione func1 Ciao1
Sono la funzione func2 Ciao2
>>> func2('Ciao')
Traceback (most recent call last):
File "<pyshell#197>", line 1, in <module>
func2('Ciao')
NameError: name 'func2' is not defined
Scoping di Variabili
Il nome di una variabile globale può essere sovrascritto da una variabile
locale. Per evitare ambiguità è utile fare ricorso allo statement global con
la sintassi global var1[,var2[,…,varN[.
Esempio
globalVar=‘abc’ globalVar=‘abc’
def stampa(): def stampa():
global globalVar
globalVar =‘def’ globalVar=‘def’
print ‘Inside stampa()’, globalVar print ‘Inside stampa()’,globalVar
print ‘Outside globalVar’, globalVar
print ‘Outside gg:’, globalVar

Output Output
Inside stampa() def Inside stampa() def
Outside gg: abc Outside gg: def
Scoping di Variabili
NOTA1:
Il caricamento di un modulo avviene tramite la keyword import.
Esistono però due modalità differenti:
import module
from module import

Nel primo caso viene importato il modulo ma non viene


modificato il namespace corrente. Nel secondo caso vengono
importate funzioni e attributi da module al namespace
corrente.
Scoping di Variabili

Esempio:
import os from os import *
os.path.basename(os.getcwd()) path.basename(getcwd())
‘Python25’ ‘Python25’
Scoping di Variabili

NOTA2:
La funzione locals() e la funzione globals() restituiscono
rispettivamente un dizionario con le variabili locali e globali
dello scope corrente.
Scoping di Variabili
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None,
'__package__': None}
>>> import math
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None,
'math': <module 'math' (built-in)>, '__package__': None}
>>> from math import *
>>> globals()
{'pow': <built-in function pow>, 'fsum': <built-in function fsum>, 'cosh': <built-in function cosh>,
'ldexp': <built-in function ldexp>, 'hypot': <built-in function hypot>, 'acosh': <built-in function
acosh>, 'tan': <built-in function tan>, 'asin': <built-in function asin>, 'isnan': <built-in function
isnan>, 'log': <built-in function log>, 'fabs': <built-in function fabs>, 'floor': <built-in function
floor>, 'atanh': <built-in function atanh>, 'sqrt': <built-in function sqrt>, '__package__': None,
'frexp': <built-in function frexp>, 'factorial': <built-in function factorial>, 'degrees': <built-in
function degrees>, 'pi': 3.141592653589793, 'log10': <built-in function log10>, '__doc__': None,
'math': <module 'math' (built-in)>, 'asinh': <built-in function asinh>, 'fmod': <built-in function
function gamma>}
Scoping di Variabili

>>> def myf():


x=5
a=3
print locals()

>>> myf()
{'a': 3, 'x': 5}
Lambda Function
Python permette la creazione di funzioni inline anonime, tramite la keyword
lambda, con la sintassi:
lambda <args> : <expression>
equivalente ad un funzione con argomenti args e con return-value expression.
Sebbene Python non sia un linguaggio funzionale, l’utilizzo delle lambda
function congiuntamente all’utilizzo di alcune funzioni built-in permette la
creazione di costrutti tipici del paradigma funzionale.
Esempio:
def power(x): return x**2
print “f:” , power(4)
g=lambda x: x**2
print “g:”, g(4)
Lambda Function

Le funzioni built-in:
• filter(function or None, sequence) -> list
• map(function, sequence,[sequence,…]) -> list
• reduce(function, sequence[, initial]) -> value
Sono utilizzate spesso in congiunzione con le funzioni lambda.
Lambda Function

• filter : applica un filtro ad una sequenza e ritorna tutti gli


elementi della sequenza dove è True la funzione

>>> l=range(10)
>>> filter(lambda x: x < 3, l)
[0,1,2]
>>>def f(x):
if(x<3): return True
>>>filter(f,l)
Lambda Function

• map : applica una funzione a tutti gli elementi di una


sequenza e ritorna gli elementi modificati secondo la funzione

>>>a=range(10); print a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>b=map(lambda x:x*3,a); print a
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
Lambda Function

• reduce: esegue un’operazione di riduzione su tutti gli


elementi di una sequenza e ritorna un singolo valore ottenuto
combinando gli elementi secondo quanto definito dalla
funzione

>>>a=range(10); print a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>b=reduce(lambda x,y: x+y,a); print b
45
>>>c=sum(a); print c
45
Lambda Function
Esempio: Prodotto Matrice-Vettore:

-Metodo 1:
def prod_mat_vect1(m,v,dim,dim2):
res=[0 for i in xrange (len(v))]
for i in xrange(dim):
for j in xrange(dim2):
res[i]+=m[i][j]*v[j]
return res
Lambda Function
-Metodo 2: eliminazione ciclo colonne
def prod_mat_vect2(m,v,dim):
res=[0 for i in xrange(dim)]
for i in xrange(dim):
res[i]=reduce(lambda x,y: x+y,map(lambda x,y: x*y, m[i][:], v))
return res
Lambda Function

-Metodo 3: eliminazione ciclo righe e colonne


def prod_mat_vect3(m,v,dim):
index=xrange(dim)
res=map(lambda i: reduce(lambda x,y: x+y,map(lambda x,y:
x*y, m[i][:], v)),index)
return res
Lambda function
Sono solitamente utilizzate per definire piccole porzioni di codice al posto dello
statement def o dove l’utilizzo degli statement non è consentito sintatticamente
o dove si necessita l’utilizzo una sola volta.
Esempio (switch with dictionary):
def plus:
return 2+2
def minus:
return 2-2
def mult:
return 2*2
d={‘plus’:plus(),’minus’:minus(), ‘mult’: mult()}

Oppure risparmiando qualche riga di codice:


d={‘plus’: (lambda :2+2) ,
’minus’: (lambda: 2-2)
‘mult’: (lambda: 2*2) }
Funzioni Ricorsive
Una funzione si definisce ricorsiva se contiene all’interno della sua definizione
una chiamata a se stessa.
Esempio

def factorial(n):
if n==0 or n==1:
return 1
else:
return n*factorial(n-1)

factorial(5)

Soluzioni alternative al calcolo del fattoriale possono includere l’utilizzo di


lambda-function e della funzione reduce come segue
Funzioni Ricorsive
Esempio:
def mult(x,y):
return x*y
def fact1_mult(n):
try:
return reduce(mult, xrange(2,n+1))
except TypeError:
return 1
def fact2_mult(n):
try:
return reduce(lambda x,y: x*y, xrange(2,n+1))
except TypeError:
return 1
f=fact1_mult(40)
f2=fact2_mult(40)
Manipolazione di File e
directory
File e Directory

Python dispone di funzionalità per gestire e manipolare files e


directories che lavorano su più piattaforme: ricercare files,
navigare in directories, manipolare, eliminare e rinominare
files.
Le funzionalità più utilizzate sono implementate nei moduli: os,
os.path, sys, shutil, glob.
File e Directory

Contenuti:

• file object: introduzione all’oggetto file, funzioni built-in e


metodi associati per la manipolazione di un file

• Handle file and directory: gestione di file e directories tramite


specifici moduli.
Object File

In Python esiste un oggetto built-in di tipo file che utilizzando le


API del sistema operativo è in grado di manipolare i file fisici
su disco.

Sono disponibili alcune funzioni built-in per le operazioni più


comuni di manipolazione dei file object.
Object File
• Creazione/Apertura di un file

La funzione file permette di creare/aprire un file object associato


al file filename.
file ( filename , [ mode , ] [ buffering ] ) → file object

La funzione built-in open crea/apre un file filename su disco e


restituisce un file object (come file)
open( filename[, mode [, buffering ]] ) → file object

Il file object dispone di metodi per la lettura/scrittura e attributi


contenenti informazioni sul file corrente.
Object File
• filename specifica il nome del file da aprire: può essere
indicato il path relativo o il path assoluto.
• mode indica la modalità di apertura del file: ‘a’, ‘w’, ‘r’.
Se il file è binario è necessario aggiungere ‘b’ al mode di
apertura: ‘rb’, ‘wb’.
Per permettere contemporaneamente operazioni di
lettura/scrittura è necessario aggiungere ‘+’ al mode di
apertura.
Solo il parametro filename è obbligatorio: se mode non viene
specificato si assume di default mode =‘r’.
• buffering specifica la dimensione desiderata del buffer. Se
negativa o omessa viene utilizzata quella di default del
sistema.
Metodi Object File
• Lettura:
Per poter usufruire di questi metodi il file deve essere aperto in
modalità ‘r’. Al procedere della lettura dei dati la posizione
all’interno del file viene incrementata.
- read ( [ size ] ) → string
Lettura di al massimo size byte. Se size è negativo la lettura
prosegue fino a EOF.
- readline ( [ size ] ) → string
Lettura della riga successiva di massimo size byte.
- readlines () → list of string
Lettura delle successive righe.

NOTA Se il file non esiste viene lanciata l’eccezione IOError.


Metodi Object File
Esempio file_read.py
try:
f=open('prova.txt','r')
l=f.readlines() print "Istanti di tempo = ", time_istant
row=len(l) print "Coordinate = ", coord
time_istant=l[0:row:4] print "Velocita' = ",v
coord=l[1:row:4] print "Accelerazione = ",a
v=l[2:row:4] except IOError,e:
a=l[3:row:4] print "Impossibile aprire file:\n ",e
finally:
f.close()
Metodi Object File
• Scrittura:
Per poter usufruire di questi metodi il file deve essere aperto in
modalità ‘w’ o ‘a’. Se il file è aperto in modalità writing la
posizione corrente è all’inizio del file, se aperto in modalità
append la posizione corrente è EOF.
- flush () flush del buffer interno di I/O
- write ( string )
- writelines ( list )
- truncate ( size ) troncamento del file alla posizione corrente (se
size non è specificato) o troncamento del file in size.
Metodi Object File
• Posizione:
Le funzioni di lettura/scrittura alterano la posizione corrente
all’interno del file. L’object-file dispone di metodi per
interrogare il file sulla posizione corrente e modificarla.
- seek ( offset , [ whence ] ) permette di spostarsi all’interno di
un file. Il primo parametro offset indica il numero di byte, il
secondo parametro whence specifica come spostarsi
all’interno del file. whence può valere:
0 (default): spostamento di offset byte dall’inizio del file
1: spostamento dalla posizione corrente di offset byte
2: spostamento di offset byte a partire da EOF.
- tell ()→ integer opera un’interrogazione sulla posizione
corrente.
Metodi Object File
• Chiusura
Un file aperto deve essere chiuso con la funzione:
- close()

Dopo aver chiuso un file, qualsiasi tentativo di agire sul file


object a cui era associato genera IOError.

• Attributi:
- closed: valore bool che indica se il file è aperto o chiuso
- name: nome del file
- mode: modalità di utilizzo del file.
Metodi Object File
try: # Esempio file_methods.py
f=open(‘file_prova.txt','w+')
for i in xrange(20):
f.write("Riga "+str(i)+'\n')
f.flush()
f.seek(0)
print f.read()
f.seek(0)
f.truncate(10)
l=f.readlines()
print l
print “Riga 100”, l[100]
except IOError:
print "Impossibile trovare il file specificato !“ Errore out of range!!
except IndexError,e:
print “Errore out of range: ” ,e
finally:
Viene eseguito finally
f.close()
Metodi Object File
Esempio test_lettura.py (efficienza nella lettura di un file di dati)
import timeit
def read_m1(name):
f=open(name)
while 1:
lines=f.readlines(10000)
if not lines:
break
for line in lines:
pass
f.close()
Metodi Object File
def read_m2(name):
f=open(name)
while 1:
line=f.readline()
if not line:
break
pass
f.close()
Metodi Object File

def read_m3(name):
f=open(name,’r’)
l=f.read()
for i in l:
pass

name=['1mb.test','5mb.test‘,’10mb.test’,’100mb.test’]
out=open('time_read.txt','a')
Metodi Object File
for i in name:
t=timeit.Timer('read_m1(i)','from __main__ import read_m1,name,i')
z=t.timeit(100)
out.write('Tempo di lettura m1 '+ str(i)+': '+str(z)+'\n')

t2=timeit.Timer('read_m2(i)','from __main__ import read_m2,name,i')


z2=t2.timeit(100)
out.write('Tempo di lettura m2 '+ str(i)+': '+str(z2)+'\n')

t3=timeit.Timer('read_m3(i)','from __main__ import read_m3,name,i')


z3=t3.timeit(100)
out.write('Tempo di lettura m3 '+ str(i)+': '+str(z3)+'\n')
out.close()
Metodi Object File

OUTPUT
Tempo di lettura m1 1mb.test: 1.41886830661
Tempo di lettura m2 1mb.test: 1.75900309599
Tempo di lettura m3 1mb.test: 5.46952516591
Tempo di lettura m1 5mb.test: 7.76999695747
Tempo di lettura m2 5mb.test: 10.1169012491
Tempo di lettura m3 5mb.test: 32.3493717795

Tempo di lettura m1 10mb.test: 16.0330113541


Tempo di lettura m2 10mb.test: 20.6929195701
Tempo di lettura m3 10mb.test: 53.6444475623

Tempo di lettura m1 100mb.test: 86.4516636965


Tempo di lettura m2 100mb.test: 174.918247282
Tempo di lettura m3 100mb.test: 1259.1668478
File Handling
Le operazioni di processamento di file e directory possono
facilmente essere eseguite tramite Python.

• Modulo os.path
Il modulo os.path dispone delle più comuni funzionalità per la
manipolazione del pathname. Tra cui le principali:
Informazioni sul pathname:
- abspath( string ) → string
- basename( string ) →string
- dirname ( string ) →string
File Handling

Esempi:
>>>os.path.abspath(‘Python’)
'C:\\Programs\\Python‘
>>>os.path.basename('C:\Programs\Python\‘ )
‘Python’
>>>os.path.dirname('C:\Programs\Python\‘)
‘C:\\Programs’
File Handling
Interrogazioni pathname:

Per verificare se un particolare path fa riferimento al nome di un


file, ad un link o ad una directory.
- isdir ( string ) → bool
- isfile ( string ) →bool
- islink ( string ) →bool
- exist ( string ) → bool

Esempio:
>>>os.path.isdir(‘C:\Program\Python\ciao.txt’)
False
File Handling
Manipolazione del pathname:
- normcase ( string ) → string
- split ( string) → (head,tail)
- join ( a, *p ) → string

Esempio:
>>>os.path.join(‘C’,’Programs’,’ciao.txt’)
‘C\\Programs\\ciao.txt’
File Handling

Informazioni sui file:


– getatime ( filename ) → int
– getctime ( filename ) → int
– getmtime ( filename ) →int
– getsize ( filename ) → int

Esempio:
>>> k=os.path.getatime('C:\Programs\tempi_range.txt')
>>> time.gmtime(k)
(2009, 4, 3, 10, 17, 40, 4, 93, 0)
Modulo os
Modulo os
Il modulo os contiene molte funzioni per la manipolazione di file,
directory, processi. L’utilizzo di questo modulo permette lo
sviluppo di applicazioni multipiattaforma garantendo la
portabilità del codice.

Muoversi tra file e directory


La funzione os.getcwd( ) ritorna la directory di lavoro corrente.
La funzione os.chdir ( path ) cambia la directory di lavoro
corrente nella directory path.
Per elencare tutti i file presenti in una directory viene utilizzata la
funzione os.listdir ( path )
File Handling
Esempio vista_cartella.py
from os import *
from os.path import *
def pprint(lista):
for el in lista: print el
def view_dir(name):
os.chdir(name)
l=os.listdir(os.getcwd())
ffile=[]
flink=[]
fdir=[]
for i in l:
if os.path.isfile(i):
ffile.append(i)
File Handling
if os.path.islink(i):
flink.append(i)
elif os.path.isdir(i):
fdir.append(i)
view_dir(i) #chiamata ricorsiva
os.chdir(name)
else:
pass
print 'List of file della directory: ', name,'\n'
pprint(ffile)
print 'List of link della directory: ', name,'\n'
pprint(flink)
print 'List of subdir della directory: ', name,'\n'
pprint(fdir)
s=raw_input('Inserire nome path: ')
view_dir(s)
File Management: Modulo os
Esempio esplora_cartelle.py
def visit(arg,dirname,names): La funzione os.path.walk
(path, visit, arg) percorre
os.chdir(dirname) l’albero delle directory
files=filter(os.path.isfile,names) con radice path e per
ogni directory chiama la
dire=filter(os.path.isdir,names)
funzione visit con
link=filter(os.path.islink,names) argomenti (arg,
print 'File in ',dirname,': ',files dirname, names).
print 'Dire in ',dirname,': ',dire
print 'Link in ',dirname,': ',link
s=raw_input('Inserire nome path: ')
os.path.walk(s,visit, ())
File Management : Modulo os
Modifica di file e directory
La funzione os.mkdir ( path [, mode=0777] ) crea una nuova
cartella di lavoro
La funzione os. makedirs( path [, mode=0777 ] ) crea le cartelle in
maniera ricorsiva.
Esempi:
>>>os.listdir(os.getcwd()) # contenuto della cartella corrente
[]
>>>os.makedirs(os.path.join(os.getcwd(),os.path.join(‘nuova’,’cartella’)))
>>>os.mkdir(os.path.join(os.getcwd(),’nuova2’))
>>>for el in os.listdir (os.getcwd()):
print os.path.join(os.getcwd(),el) #os.path.abspath(el)
os.listdir(os.path.join(os.getcwd(),el))
File Management : Modulo os

Per eliminare una directory si utilizza la funzione os.rmdir(path),


per procedere in maniera ricorsiva si utilizza la funzione
os.removedirs(path).
La funzione os.remove (path) permette invece la cancellazione di
un file.

Esempio: eliminare in maniera ricorsiva il contenuto di una


cartella
def delete(arg,dirname,names):
os.chdir(dirname)
files=filter(os.path.isfile,names)
dire=filter(os.path.isdir,names)
File Management : Modulo os
for f in files:
remove(f)
for d in dire:
try:
removedirs(d)
except WindowsError:
os.path.walk(os.path.join(dirname,d),delete,arg)
try:
if len(filter(os.path.isfile,os.listdir(arg)))==0:
os.chdir(arg)
d=os.listdir(arg)
for i in d: rmdir(i)
except:
pass
l=raw_input(‘Inserisci path: ‘)
os.chdir(l)
os.path.walk(os.getcwd(),delete,l)
File Management
La funzione rename (old,new) permette di rinominare file e/o
cartelle.
La funzione renames (old,new) si comporta allo stesso modo in
maniera ricorsiva.
La funzione os.stat( path ) restituisce una tupla con le
informazioni inerenti al path (dimensione, ultimo accesso,
ultima modifica, permessi).
Per cambiare i permessi di accessi ad un a file o ad un cartella il
modulo os dispone della funzione os.chmod (path, mode).
Esempi:
# write-read-exe for user, read for group and other
>>>os.chmod(‘prova.txt’,0744)
# write only for user
>>>os.chmod(‘prova.txt’,0400)
Modulo Shutil
In aggiunta al modulo os, il modulo shutil offre operazioni di alto
livello per la manipolazione di files. In particolare sono
supportate funzioni per la copia e la rimozione di files.
Copia di file
shutil.copy ( src, dst ) copia il contenuto di src in dst (file e
directory)
shutil.copyfile ( src, dst ) copia il contenuto di src in dst
shutil.copymode ( src, dst ) copia dei permessi da src a dst
shutil.copystat ( src, dst ) copia dei permessi e dei tempi di
ultimo accesso/modifica da src a dst.
shutil.copytree ( src, dst [,symlinks] ) copia ricorsivamente un
intero albero avente radice src.
Modulo Shutil
Rimozione di file
shutil.rmtree( path[, ignore_errors[, onerror]] ) cancella un
intero albero di directory.
shutil.move ( src, dst ) sposta ricorsivamente un file o una
cartella.
Modulo Shutil
Esempio:

from os import * ; from shutil import *


import time , stat
def file_info(name):
info=os.stat(name)
print 'fMode ', format(info.st_mode,"o")
print 'fCreated ',time.ctime(info.st_ctime)
print 'fAccess ', time.ctime(info.st_atime)
print 'fModify ', time.ctime(info.st_mtime)
Modulo Shutil
def read(name):
f=file(name)
while 1:
l=f.readline()
if not l: break
print l
f.close()
Modulo Shutil

file1=raw_input(‘Inserisci nome file: ‘)


file2=raw_input(‘Inserisci nome file2: ‘)
print ‘:::::::::::::::STAT BEFORE:::::::::::::::'
file_info(file1)
print ‘:::::::::::::::read before:::::::::::::::'
read(file1)
print ‘:::::::::::::::Copy\n:::::::::::::::'
shutil.copy(file2,file1)
print ‘:::::::::::::::STAT AFTER:::::::::::::::: ‘
file_info(file1)
Modulo Shutil
#OUTPUT
C:\Documents and Settings\user\Desktop\Python_corso\New
:::::::::::::::stat before:::::::::::::::
fMode 33206
fCreated Wed Apr 15 12:02:48 2009
fAccess Wed Apr 15 16:22:11 2009
fModify Thu Jan 15 11:24:01 2009
::::::::::::::: read before :::::::::::::::
new new
::::::::::::::: Copy :::::::::::::::
::::::::::::::: AFTER: :::::::::::::::
fMode 33206
fCreated Wed Apr 15 12:02:48 2009
fAccess Wed Apr 15 16:23:32 2009
fModify Wed Apr 15 15:32:07 2009
Modulo Glob
Python mette a disposizione il modulo glob per la
manipolazione del path name. Il modulo glob opera
utilizzando le regole di espansione della UNIX shell : * , ? , [
seq ].

• glob.glob( pathname )  list


La funzione glob ritorna la lista dei path che coincidono con la
stringa pathname.

• glob.iglob( pathname )  iterator


La funzione iglob ha la stessa funzionalità della funzione
glob(), ma ritorna un iteratore anziché una lista di valori.
Modulo Glob
Esempio:
>>> import os
>>> import glob
>>>os.listdir(‘.’)
[‘1file.txt’, ‘2.file.log’]
>>> glob.glob(‘*file.*’)
[‘1file.txt’,’2.file.log’]
>>>glob.glob(‘[0-1]file.*’)
[‘1file.txt’]
>>>glob.glob(‘?file.txt’)
[‘1file.txt’]
>>> it = glob.iglob(‘*file.*’)
Modulo Glob
Esempio
>>>try:
while True: print it.next()
except StopIteration: pass

1file.txt
2.file.log
Modulo Glob
• glob.fnmatch.fnmatch( filename,pattern ) bool
La funzione fnmatch verifica che il filename corrisponda a
pattern. Il confronto è case-insensitive. Per un confronto case-
sensitive può essere utilizzata la funzione
glob.fnmatch.fnmatchcase

• glob.fnmatch.filter ( names,pattern )  list


La funzione ritorna un sottoinsieme della lista di names che
soddisfano pattern
Modulo Glob
Esempio:

l=[]
for ffile in os.listdir(‘.’):
if glob.fnmatch.fnmatch(ffile, ‘*.txt’):
l.append(ffile)
#OPPURE
l=glob.fnmatch.filter(os.listdir(‘.’),’*.txt’)
Python per il calcolo
scientifico
Indice
• Numpy
• Creazione di un array
• Reshape & Resize
• Indexing
• Operatori Aritmetici
• ufunc
• Broadcasting
• Cenni di vettorizzazione
• Efficienza e loop
• Array Copy
• Attributi e Metodi
• I/O con Array Numpy
• Matrix
Python in ambito scientifico
Python è diventato accessibile a nuovi gruppi di utilizzatori. A dispetto della
sua semplicità è un linguaggio abbastanza potente da permettere la
gestione di applicazioni complesse
• Python ha una posizione forte nell’ambito del Computing Scientifico:
– E’ open-source!
– Ci sono significative e diffuse comunità di utenti, quindi è piuttosto
facile reperire in rete aiuto e documentazione
– Ad oggi è disponibile un esteso ecosistema di environment, di package
e di librerie scientifiche per Python ed è in rapida crescita
– Si possono scrivere codici Python che ottengono ottime performance,
grazie alla forte integrazione con prodotti altamente ottimizzati scritti
in C e Fortran (BLAS, Atlas, Lapack, Intel MKL®, …)
– E’ possibile sviluppare applicazioni parallele che usino la
comunicazione interprocesso (MPI), il multi-threading (OpenMP) ed il
GPU computing (OpenCL e CUDA)
Python in ambito scientifico
A livello mondiale è sfruttato in ambito scientifico (e non solo) in diversi
progetti:
• National Space Telescope Laboratory (Hubble Space Telescope)
• Lawrence Livermore National Laboratories (pyMPI)
• Enthought Corporation (Applicazioni Geofisiche ed Elettormagnetiche)
• Anaconda (RedHat Linux Installer)
• Google
Contenuti
Nello specifico verranno trattati:

• Introduzione base al modulo numpy: definizione di array e operazioni


base.
Scaricabile:
https://sourceforge.net/projects/numpy/files/
Official web-site:
http://numpy.scipy.org/

• Introduzione al modulo pylab: creazione di grafici


Official web-site:
http://matplotlib.sourceforge.net/
Numpy

“Life is too short to write C++ code“


David Beazley - EuroScipy 2012 Bruxelles
NumPy

NumPy è l’abbreviazione di Numerical Python: un’estensione del linguaggio


pensata per l’ottimizzazione della gestione di grosse moli di dati, utilizzata
principalmente in ambito scientifico.
Il modulo NumPy ingloba le features dei moduli Numeric e Numarray e aggiunge
nuove funzionalità.
• In Python puro abbiamo a disposizione:
– oggetti numerici di alto livello: interi, floating point
– container: liste (insert ed append a costi bassi) e dizionari (operazioni
di lookup veloci)
• NumPy
– Introduce un modalità naturale ed efficiente per utilizzare array multi-
dimensionali
– Aggiunge una serie di utili funzioni matematiche di base (algebra
lineare, FFT, random number, …)
Perché usare Numpy
• In NumPy funzioni e metodi agiscono su interi vettori e matrici, quindi
raramente si ricorre a loop espliciti, tipicamente poco efficienti
• Gli algoritimi utilizzati sono estremamente testati e disegnati con
l’obiettivo di fornire alte prestazioni
• NumPy gestisce lo store degli array in memoria in modo molto più
efficiente, rispetto a quanto accade in Python puro per le liste e le liste di
liste
• Le operazioni di I/O di array sono significativamente più veloci
• File di grandi dimensioni possono essere memory-mapped, consentendo
così una lettura/scrittura ottimale di grosse moli di dati
• Molte parti di NumPy sono scritte in C, cosa che rende un codice NumPy
più veloce dell’analogo in Python puro
• NumPy prevede una C API, che consente di estenderne le funzionalità
(questo tema non viene trattato in questa introduzione a NumPy)
Quando non usare Numpy

• Al di fuori del contesto del calcolo tecnico scientifico, l’uso di NumPy è


meno utile

• Principali limitazioni:
– NumPy non è supportato per lo sviluppo di applicazioni Google App
Engine, perché molte sue parti sono scritte in C
– Gli utenti di Jython – l’implementazione Java di Python – non posso
contare sul modulo NumPy: Jython gira all’interno di una Java Virtual
Machine e non può importare NumPy, perché molte sue parti sono
scritte in C
Import del modulo
• Importiamo il modulo come di consueto

>>> import numpy


>>> from numpy import *
>>> import numpy as np #default in molti codici e nella documentazione
numpy

Numpy fornisce un nuovo tipo di dato: un array N-dimensionale (ndarray).


Organizzazione del modulo
ndarray
• NumPy fornisce il nuovo oggetto ndarray

• ndarray è una struttura dati omogenea (fixed-type) e multi-dimensionale.

Teminologia:
Con size di un array intendiamo il numero di elementi presenti in un array.
Con rank di un array si intende il numero di assi/dimensioni di un array.
Con shape di un array intendiamo le dimensioni dell’array, cioè una tupla di
interi contenente il numero di elementi per ogni dimensione, numero
VARIABILE.
L’ itemsize rappresenta la dimensione in memoria di ogni singolo elemento
dell’array.
ndarray
La definizione del ndarray numpy/core/include/numpy/ndarrayobject.h

typedef struct PyArrayObject {


PyObject_HEAD
char *data; // pointer to raw data buffer
int nd; // number of dimensions, also called ndim
npy_intp *dimensions; // size in each dimension
npy_intp *strides; // bytes to jump to get to the next
//element in each dimension
PyObject *base;
PyArray_Descr *descr; /* Pointer to type structure */
int flags; /* Flags describing array */
PyObject *weakreflist; /* For weakreferences */
} PyArrayObject;
ndarray
Array Data Structure
N
DAr
rayDa
taSt
ruc
tur
e

The float32 data type describes the


dtype * float32 array data elements
dim count 2
12 bytes
dimensions 3 3
4 bytes Memory View
strides 12 4
data * 0 1 2 3 4 5 6 7 8
M
emo
ryblo
ck

0 1 2
P
yt
h o
nVie
w: Python View
3 4 5
6 7 8
54
Creazione di un Array

Creazione di un array
Ci sono diverse modalità per generare un array. La più semplice consiste
nell’utilizzo della funzione
array(object, dtype=None, copy=1,order=None)  array

NOTA: allocazione in memoria per array multidimensionali.


Creazione di un Array
• Il modo più semplice per creare un array è di convertire altre strutture dati
Python

>>>import numpy as np
>>>a=np.array([1,2,3,4])
>>>lista1=[1,2,3,4]
>>>tupla=(5,6,7,8)
>>>a=np.array(lista) #from a list
>>>b=np.array(tupla) #from a tupla
>>>c=np.array([lista,tupla]) #from a list and from a tupla
>>>a.dtype
dtype('int32')
ndtype
• Ci sono 21 built-in data type che possono essere usati per creare un array.
• Numpy supporta più tipi di dato rispetto a python puro.
Type Description
bool Boolean
int Platforma integer
int8 Byte (-128,127)
int16 Integer (-32768,32767)
int32 Integer (-2147483648, 2147483647)
int64 Integer (-9223372036854775808, 9223372036854775807)

uint8 unsigned integer (0,255)


uint16 unsigned integer (0,65535)
uint32 unsigned integer (0,4294967295)
uint64 unsigned integer (0,18446744073709551615)
float float64
float32 Single precision float
float64 Double precision float
complex complex128
complex64 Complessi con 2 32-bits float
complex128 Complessi con 2 64-bits float
ndtype
• E’ possibile creare array con nuovi dtype definiti dall’utente.

>>>dt=dtype([('Name','S3'),(‘Anni', numpy.int64)])
>>a=array([('Chiara',3),('Marco',4)],dtype=dt)
>>> a
array([('Chi', 3L), ('Mar', 4L)],
dtype=[('Name', '|S3'), ('Val', '<i8')])
>>>dt=dtype({'names':('Name','Anni','Cognome'),'formats':('S3','int64','S3')})
>>>a=array([('Chiara',3,'Bianchi'),('Marco',4,'Rossi')],dtype=dt)
>>> a
array([('Chiara', 3L, 'Bia'), ('Marco', 4L, 'Ros')],
dtype=[('Name', '|S10'), ('Anni', '<i8'), ('Cognome', '|S10')])
Dimension e shape
int nd; /* number of dimensions, also called ndim */
npy_intp *dimensions; /* size in each dimension */

• L’attributo shape specifica la forma dell’array:


import numpy as np
a=np.array([[1,2],[2,2]])
a.shape
(2,2)
b=np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
b.shape
(2,2,2)
• L’attributo ndim specifica la dimensione dell’array
a.ndim
2
b.ndim
3
itemsize

L’itemsize ritorna la dimensione di ciascun elemento


>>>b=array([[1, 2,3],[3, 4,5]])
>>> b.itemsize
4
>>> b.dtype
dtype('int32')
>>> b.strides #bytes to jump to get to the next element
#of each dimension
(12, 4) #skyp_byte_row, skype_byte_col
>>>c=array([[1,2,3],[4,5,6]],order='Fortran')
>>> c.strides
(4, 8)
Creazione di un Array
Se il contenuto di un array è a priori ignoto, è utile utilizzare funzioni per
riempire in modo sistematico l’array.

• La funzione zeros crea un array di dimensioni shape di soli zeri.


zeros( shape, dtype=float, order =‘C’ )

• La funzione ones crea un array di dimensione shape di soli uni.

ones( shape,dtype=None, order =‘C’ )


Creazione di un Array
• La funzione empty crea un array di dimensioni shape senza inizializzazione.
empty( shape, dtype=None, order =‘C’ )

• La funzione identity genera la matrice identità n X n


identity( n, dtype=None )

• La funzione eye crea una matrice N X M riempendo di 1 la k-esima


diagonale
eye( N, M=None, k=0, dtype=float )
Creazione di un Array
In maniera analoga alla funzione range per le liste, esiste la possibilità di
creare una sequenza di numeri anche per gli array.
arange( [start,] stop[, step,], dtype=None )
• La funzione arange genera un array di numeri compresi tra start e stop con
passo step.
In alternativa:
linspace( start, stop, num=50, endpoint=True, restep=False )
• Genera una sequenza di num numeri uniformemente distribuita compresa
tra start e stop.
Creazione di un Array
• E’ possibile creare array anche da stringhe con la funzione fromstring

>>> np.fromstring('1 2', dtype=int, sep=' ')


array([1, 2])
>>> np.fromstring('1, 2', dtype=int, sep=',')
array([1, 2])

• E’ possibile creare array leggendo i dati da disco…


Reshape & Resize
Reshaping & Resizing Array
I metodi resize e reshape permettono di modificare la forma e la dimensione
dell’array. Il metodo
reshape(shape, order=‘C’)
Restituisce una nuova struttura dati ridistribuendo gli elementi dell’array secondo la
nuova forma shape con ordine order.
La funzione reshape deve lasciare invariato il numero di elementi dell’array.

La funzione
resize(new_shape, refcheck=True, order=False)
Lavora direttamente in-place e permette di modificare la forma dell’array e di
ridimensionarlo.
Resize funziona solo se l’array non è referenza o non è referenziato.
Esempio
>>>a=arange(20)
>>>a.resize(5,6) #Ok
Reshape & Resize
>>>b=a
>>>a.resize(3,3) #Error a is referenced by b
Traceback (most recent call last):
File "<pyshell#160>", line 1, in <module>
a.resize(3,3)
ValueError: cannot resize an array that has been referenced or is referencing
another array in this way. Use the resize function
Esempio
>>>a=array(range(1,9))
>>>print “Shape” , a.shape
>>>c_style=a.reshape((2,2,2),order=‘C’) #Array Method: Numpy Style
>>>f_style=reshape(a,(2,2,2),order=‘F’) #Numpy Function
>>>print “C-style “, c_style
>>>print “Fortran-style “, f_style
>>>c_style=c_style.reshape((2,4))
>>>print “Reshape c_style“, c_style
>>>f_style=f_style.reshape((2,4))
>>>print “Reshape f_style”,f_style
Reshape & Resize
OUTPUT
Shape (8,)

C-style c_style [ [ [1, 2],


[3, 4] ],
[ [5, 6],
[7, 8] ] ]

Fortran-style f_style [ [ [1,5],


[3, 7] ]
[ [2, 6],
[4, 8] ] ]

Reshape c_style [[1, 2, 3, 4],


[5, 6, 7,8]]

Reshape f_style [[1, 5, 3, 7],


[2, 6, 4,8]]
Indexing - Slicing - Iteration
Array Indexing e Slicing
L’accesso agli elementi di un array avviene tramite l’operatore [] . Anche sugli
array è applicabile l’operatore di slicing [:] .
Nel caso di array monodimensionali si adotta la stessa notazione delle liste.
Esempio
>>>a=ones(4)
>>>print “a[0] “, a[0]
a[0] 2.0
>>>print a [1:3]
>>>[ 3., 4.]
Indexing – Slicing - Iteration
Nel caso di array multidimensionali:
Esempio
>>>a=array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
>>> print a[0][0]
1
>>> print a[0,0]
1
>>> print a[2]
[7 8 9 ]
>>> print a[:,1]
a[ 2 , 5 , 8 , 11 ]
>>> print a[2:,1:3]
[[ 8 9]
[11 12]]
Indexing – Slicing - Iteration
>>> a=arange(25)
>>> a=a.reshape((5,5))
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
>>> a[::,1]
array([ 1, 6, 11, 16, 21])
>>> a[1]
array([5, 6, 7, 8, 9])
>>> a[1,:]
array([5, 6, 7, 8, 9])
>>> a[1,::]
array([5, 6, 7, 8, 9])
>>> a[1,::2]
array([5, 7, 9])
>>> a[1,5::-1]
array([9, 8, 7, 6, 5])
Array Copy
NOTA
• Lo slicing per gli array è profondamente differente rispetto alla slicing per le liste.
Nel caso di array il sotto-array generato mediante slicing è una reference all’area
originale di memoria. Un operazione di slicing crea una view dell’array originale,
ovvero solo un modo differente per accedere ai dati dell’array. Quando viene
modificata la view, viene modificato l’array originale
Nel caso delle liste la sotto-lista è una copia per valore della lista originale.
>>>a=arange(6) >>>a=range(6)
>>>b=a[2:5] >>>b=a[2:5]
>>>b[0]=40 >>>b[0]=40
>>>b[1]=50 >>>b[1]=40
>>>print “a:“, a , “b:”, b >>>print “a:“, a , “b:”, b
a: [ 0 1 40 50 4 5] b: [40 50 4] a: [ 0 1 2 3 4 5] b: [40 50
4]
Array Copy

NOTA (copia per riferimento e per valore)


Nel caso degli array la copia è di default per referenza.
>>>a=arange(5)
>>>b=a
>>>b[0]=100
>>>print “a:”, a , “b:” , b
a: [100,1,2,3,4] b: [100,1,2,3,4]
Array Copy

Per effettuare un assegnamento per valore, si utilizza la funzione copy


>>>c=a.copy()
>>>print “id(c): “, id(c), “id(a):”, id(a)

id(a): 18820584 id(c): 21335648


>>>c[0]=100
>>>print “c” , c , “a”, a
c [100, 1, 2, 3, 4] a [0, 1, 2, 3, 4]
Indexing – Slicing - Iteration
• La selezione degli elementi di un array può avvenire in modo più
complesso:
– Attraverso un array di indici
– Tramite una maschera booleana
Questo metodo è noto come fancy indexing

>>> x = np.arange(10,1,-1)
Il nuovo array:
array([10, 9, 8, 7, 6, 5, 4, 3, 2]) ➔ ha la shape dell’array di indici
A=x[np.array([3,3,2,8])] ➔ ha tipo e valori dell’array di partenza

array([7, 7, 8, 2])
>>> AA=x[np.array([[3,3],[2,8]])]
array([[7, 7],
[3, 2]])
Indexing – Slicing - Iteration
Usando una maschera booleana
>>> y
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17]])
>>> y[y>10]
array([11, 12, 13, 14, 15, 16, 17] # 1d array

a= array([10, 3, 8, 0, 19, 10, 11, 9, 10, 6])


Modifica degli elementi di un
>>> a[a % 3 == 0] = -2 array con fancy indexing
>>> a
array([10, -2, 8, -2, 19, 10, 11, -2, 10, -2])
Altri fancy indexing illustrati

36
Indexing – Slicing - Iteration

Iteration
L’iterazione sugli elementi di un array può essere effettuata in diversi modi.
Attraverso il classico ciclo for lungo gli assi dell’array:
Esempio:
>>>a=arange(9)
>>>a.shape=(3,3)
>>>for i in xrange(a.shape[0]):
for j in xrange(a.shape[1]):
a[i,j]=i+j
Il ciclo for applicato invece agli elementi di un array agisce di default sul
primo asse.
Indexing – Slicing - Iteration
Esempio:
>>>for el in a:
print el
>>>[ 0 1 2]
[ 1 2 3]
[ 2 3 4]

Attraverso l’iteratore flat su ogni elemento dell’array.

Esempio:
>>> for i in a.flat:
print i
012123234
L’operazione di iterazione sugli array risulta tuttavia poco efficiente computazionalmente.
Per le operazioni sugli array Python dispone di funzioni scritte in C che operano
direttamente sull’intero array…SEGUE
Operazioni numeriche su array
• Operazioni elementari con scalari:
>>> a = np.array([1, 2, 3, 4])
>>> a + 1 # somma di uno scalare
array([2, 3, 4, 5])
>>> a – 2 # sottrazione di uno scalare
array([-1, 0, 1, 2])
>>> 3*a # moltiplicazione per uno scalare
array([3, 6, 9, 12])
>>> 2**a # potenza (base scalare)
array([2, 4, 8, 16])
>>> a = np.array([1., 2., 3., 4.])
>>> a/2 # divisione per uno scalare
array([0.5, 1., 1.5, 2.])
Operatori Aritmetici
Operatori aritmetici
Gli operatori aritmetici agiscono in maniera elementwise sugli array.
Questa regola si applica sia ad operatori unari che ad operatori binari. E’ inoltre
valida per funzioni matematiche unarie (sin, cos, etc.)

>>> a = np.array([1, 2, 3, 4])


>>> b = np.ones(4) + 1
>>> a – b # sottrazione tra array
array([-1., 0., 1., 2.])
>>> a*b # moltiplicazione tra array
array([2., 4., 6., 8.])

>>> a += 1; # autoincremento di array


array([2, 3, 4, 5])

>>> 2**(a+1) – a # espressione con array


array([ 6, 13, 28, 59])
Operatori Aritmetici
Esempio
b=array([5,6,7,8])
c=arange(1,5)
d=c+b
print “Somma “ ,b,”+”,c, “= “, b+c
b+=1
print “Autoincremento b +=1 b=“, b
print “Moltiplicazione c*3 ” ,c, “* 3= ”,c*3
print “Sin (c)”, sin(c)
OUTPUT
Somma [5,6,7,8] + [1,2,3,4] = [6,8,10,12]
Autoincremento b+=1 b= [6,7,8,9]
Moltiplicazione c*3 [1,2,3,4] *3 = [3,6,9,12]
Sin(c) [ 0.84147098, 0.90929743, 0.14112001, -0.7568025 ]
Operatori di confronto
>>> a = np.array([1, 2, 3, 4])

>>> b = np.array([4, 2, 2, 4])

>>> a == b Anche gli operatori standard di


array([False, True, False, True])
confronto lavorano elementwise
>>> a > b
array([True, False, True, True])

>>> c = np.array([1, 2, 3, 4])

>>> np.array_equal(a,b)
False Per un confronto arraywise si
usa il metodo array_equal
>>> np.array_equal(a,c)
True
Operatori logici

>>> a = np.array([1, 1, 0, 0], dtype=bool)

>>> b = np.array([1, 0, 1, 0], dtype=bool)

>>> np.logical_or(a,b)
array([True, True, True, False], dtype=bool)

>>> np.logical_and(a,b)
array([True, False, False, False], dtype=bool)
ufunc
• Numpy oltre alla definizione dell’oggetto ndarray definisce anche le
funzioni universali ufunc
• Le funzioni ufunc permettono di operare elemento- elemento ,
sull’intero array senza dover usare dei loop espliciti.
• Queste funzioni sono dei wrapper a delle funzioni del core numpy
tipicamente sviluppate in C o Fortran

>>>a=numpy.arange(100)
>>>b=numpy.cos(a)
ufunc
Esempio della funzione ufunc_loop definita nel core di Numpy
void ufunc_loop(void **args, int *dimensions, int *steps,
void *data)
{
char *input_1 = (char*)args[0];
char *input_2 = (char*)args[1];
char *output = (char*)args[2];
int i;
for (i = 0; i < dimensions[0]; ++i) {
*output = elementwise_function(*input_1, *input_2);
input_1 += steps[0];
input_2 += steps[1];
output += steps[2];
}
}
ufunc
• Ci sono più di 60 ufuncs
• Alcune ufunc sono nascoste dietro gli operatori aritmetici: i.e.
np.multiply(x,y)
è chiamata quando si effettua l’operazione a*b
• NumPy offre funzioni trigonometriche, esponenziali and logaritmiche etc
etc. Alcuni esempi:

>>> b = np.sin(a)
>>> b = np.arcsin(a)
>>> b = np.sinh(a)
>>> b = a**2.5 # power function
>>> b = np.log(a)
>>> b = np.exp(a)
>>> b = np.sqrt(a)
ufunc

• Funzione di confronto:
greater, less, equal, logical_and/_or/_xor/_nor, maximum, minimum, ...
>>> a = np.array([2,0,3,5])
>>> b = np.array([1,1,1,6])
>>> np.maximum(a,b)
array([2, 1, 3, 6])

• Funzioni floating point:


floor, ceil, isreal, iscomplex, ...
Broadcasting

• Visto che tutte le tipologie di operatori tra array lavorano elementwise, gli array
operandi devono avere (sempre) la stessa shape
>>> a = np.arange(4)
>>> a + np.array([1,2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (4) (2)
• E possibile talvolta operare con array che non hanno le stesse dimensioni

c=arange(1,5)
d=array([[1,1,1,1],[2,2,2,2]])
print d, ”+”, c “= “ d+c stretch

Questa modalità operativa viene definita di broadcasting


Broadcasting

• Il broadcasting quando applicabile permette di trattare con array che non


hanno le stesse dimensioni.

• Il broadcasting segue due regole:

– Se gli array non hanno lo stesso numero di dimensioni, l’array più


piccolo viene ridimensionato (aggiungendo dimensione ‘1’ ) fino a che
entrambi gli array non hanno la stessa dimensione.
– Array con dimensione ‘1’ lungo una particolare direzione si
comportano come l’array più grande lungo quella dimensione. Il valore
è ripetuto lungo la direzione di broadcast.
Broadcasting
• Sugli array 1d si può sempre usare il broadcast.
a=np.array([1,2,3])
a.shape # (3,)
a 1 2 3 1 2 3 b = 2 4 6 c
b=np.array([[1,2,3],[4,5,6]])
1 2 3 4 5 6 5 7 9
b.shape #(2,3)
c=a+b #OK!! Broadcastable

a=arange(6)
a=a.reshape((2,1,3))
a + b = c
b=arange(8)
b=b.reshape((2,4,1))
c=a+b #OK!! Broadcastable

a=a.arange(30)
a=a.reshape((2,5,3))
b=arange(8)
b=b.reshape((2,4,1)) #No Broadcastable
Array performance
• Le funzioni Numpy sono efficienti per lavorare sugli array, ma possono lavorare
anche sugli scalari.
• Le funzioni contenute in math sono più efficienti sugli scalari rispetto alle funzioni
Numpy.

>>> t=timeit.Timer('math.sin(math.pi)','import math')


>>> t.timeit()
0.20885155671521716 7 times faster
t=timeit.Timer('np.sin(np.pi)','import numpy as np')
>>> t.timeit()
1.3546336814836621
Efficienza di Calcolo
I cicli for non sono perfomanti in Python. Evitare di utilizzarli se non necessari!
def for_array(a):
for i in xrange(a.shape[0]):
for j in xrange(a.shape[1]):
a[i,j]=3*a[i,j]+1

def no_loop(a):
a=a*3+1

Tempi di calcolo:
for_array on 1000 X 1000 array = 3.52 s
no_loop on 1000 X 1000 array = 0.015 s
Efficienza di Calcolo

Per la stessa motivazione le strutture dati array risultano più efficienti delle liste.

def somma_array(v1,v2):
v=v1+v2
def somma_liste(l1,l2):
l=[]
for i in xrange(len(l1)):
l.append(l1[i]+l2[i])

Tempo di calcolo:
somma_array(v1,v2) con 10^7 elementi = 0.05 s
somma_liste(l1,l2) con 10^7 elementi = 10.49 s
Cenni di vettorizzazione
I cicli for sono piuttosto lenti in Python. Uno dei vantaggi nell’utilizzo degli
array consiste nel fatto che molte operazioni possono essere svolte
evitando loop espliciti questo procedimento prende il nome di
vettorizzazione.

Esempi:
VECTORIZED VERSION SCALAR VERSION
a=arange(0,4*pi,0.1) Anziché y=zeros(len(a))
y=sin(a)*2 for i in xrange(len(a)):
y[i]=sin(a[i])*2

In alcuni casi è necessario vettorizzare esplicitamente l’algoritmo:


• Direttamente: vectorize(function) # piuttosto lento!
• Manualmente: con tecniche opportune, p.e. slicing
Cenni di vettorizzazione
Solo in alcuni casi è possibile vettorizzare un’espressione:
ESEMPIO:
def func(x):
if x<0: return 1
else: return sin(x)
func(3)
func(array([1,-2,9]))
Traceback (most recent call last):
ValueError: The truth value of an array with more than one element is
ambiguous. Use
a.any() or a.all()
Versione scalare per lavorare con gli array:
Cenni di vettorizzazione
def func_NumPy(x):
r = x.copy() # allocate result array
for i in xrange(size(x)):
if x[i] < 0:
r[i] = 0.0
else:
r[i] = sin(x[i])
return r

• Implementazione penalizzante: molto lenta in Python


• Funziona solo per array monodimensionali
Utilizzo dello statement where
Cenni di vettorizzazione
def f(x): def f_vectorized(x):
if condition:
x1 = <expression1>
x = <expression1> x2 = <expression2>
else:
return where(condition, x1, x2)
x = <expression2>
return x

Nel caso precedente


def func_NumPyV2(x):
return where(x < 0, 0.0, sin(x))
• Evito l’utilizzo di cicli for
• Funziona su strutture dati multidimensionali
Cenni di vettorizzazione
Lo slicing di array è spesso utilizzato per la vettorizzazione di operazioni. In
ambito scientifico, per esempio, per applicazioni che riguardano schemi
alle differenze finite o processamento di immagini è comune incontrare
schemi del tipo:

xk  xk 1  2 xk  xk 1 k=1,2,…,n-1

Che possono essere trattati tramite funzioni scalari con

for i in xrange(1,len(x)-1):
x[i]=x[i-1]+2*x[i]+x[i+1]

Oppure tramite vettorizzazione con:

x[1:n-1]=x[0:n-2]+2*x[1:n-1]+x[2:n]
Attributi e Metodi
L’object class array dispone di utili funzionalità implementate come propri metodi e
attributi.

Attributi:
Gli attributi della classe array forniscono informazioni sulla struttura dell’array. Si
ricordano:
• dtype tipo di dato
• flat array di (rank-1)
• itemsize e nbytes bytes usati da ogni singolo elemento e dall’intero array
• ndim numero di dimensioni dell’array
• size numero totale di elementi nell’array
• shape forma dell’array

Metodi
I metodi built-in implementano funzionalità che operano direttamente sull’array. Si
ricordano:
Attributi e Metodi
take(indices, axis=None, out=None, mode='raise')
La funzione take estrapola un sottoarray costituito dagli elementi in posizione
indices secondo l’asse axis
Esempio:
>>>a=arange(27)
>>>a.resize(3,3,3)
>>>y=a.take([0,2],axis=1)
>>>y2=a.take([0,2],axis=2)
>>>y3=a.take([0,2],axis=0)
Attributi e Metodi
choose(choices, out=None, mode='raise')
La funzione choose restituisce un array costruito dalle choices sulla base di un
filtro selettore.

compress(condition,axis=None, out=None)
La funzione compress restituisce un array di elementi che soddisfano
condition lungo l’asse axis
Attributi e Metodi
fill(value)
La funzione fill riempe l’array con il valore value.
sort(axis=-1, kind=‘quicksort’, order=None)
La funzione sort riordina inplace i valori dell’array lungo axis con il metodo
kind.
transpose(*axis)
La funzione transpose traspone gli elementi dell’array secondo la
permutazione specificata da axis.

Per una lista completa di metodi e attributi si faccia riferimento a:


http://www.scipy.org/Numpy_Example_List
Operazioni di riduzione
>>> x = np.array([[1, 1], [2, 2]])
>>> x.sum(axis=0) # somma per colonna
array([3, 3])
>>> x.sum(axis=1) # somma per riga
array([2, 4])
>>> x.sum() # somma sull’intero array
6
• Tutte le altre operazioni di riduzione di array lavorano come la somma
• Sono disponibili operatori di riduzione di array di diverse tipologie:
• Statistica: ndarray.mean(), ndarray.std(), ndarray.median(), …
• Calcolo di estremi: ndarray.max(), ndarray.min(), ndarray.argmax() – ritorna l’indice
dell’elemento massimo -, ndarray.argin()…
• Logiche: ndarray.all() – ritorna True se tutti gli elementi dell’array, o lungo un asse,
sono True – e ndarray.any() – ritorna True se almeno un elemento dell’array, o di
un suo asse, è True
Valutare una funzione 2D su griglia
• Nelle scienze computazionali spesso capita di dover valutare una funzione su
una griglia
• Data la griglia dei punti (xi,yi) con xi = [0, 1, 2, 3] e yi = [0, 1, 2, 3], vogliamo
calcolare, per ciascun punto della griglia, il valore di una funzione f(x,y)
• Ingenuamente, potremmo pensare ad una soluzione NumPy come questa:

>>> x = np.arange(4)
>>> y = np.arange(4)
>>> def f(x,y):
... return x**2+y
>>> f(x,y)
array([0, 2, 6, 12])

ma, il risultato è lungi da essere quello aspettato!

64
La soluzione: meshgrid

>>> x = np.arange(4)
>>> y = np.arange(4)
>>> xx, yy = np.meshgrid(x,y)
>>> xx
array([[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3]])
>>> yy
array([[0, 0, 0, 0],
[1, 1, 1, 1], >>> f(xx,yy)
[2, 2, 2, 2], array([[ 0, 1, 4, 9],
[3, 3, 3, 3]]) [ 1, 2, 5, 10],
>>> def f(x,y): [ 2, 3, 6, 11],
... return x**2+y [ 3, 4, 7, 12]])

65
I/O con Array Numpy
• Si possono usare le funzioni eval e repr per scrivere e leggere in formato
ASCII
a = linspace(1, 21, 21)
a.shape = (2,10)
# ASCII format:
file = open(’tmp.dat’, ’w’)
file.write(’Here is an array a:\n’)
file.write(repr(a)) # dump string representation of a
file.close()
# load the array from file into b:
file = open(’tmp.dat’, ’r’)
file.readline() # load the first line (a comment)
b = eval(file.read())
file.close()
I/O con Array Numpy
• L’I/O su file può essere anche gestito loadtxt e savetxt

Lettura:
numpy.loadtxt(fname, dtype=<type
'float'>, comments='#', delimiter=None, converters=None, skiprows=0,
usecols=None, unpack=False, ndmin=0)

Scrittura:
numpy.savetxt(fname, X, fmt='%.18e', delimiter='
', newline='\n', header='', footer='', comments='#)
I/O con Array Numpy
• Text.txt

>>>a = loadtxt('textfile.txt',skiprows=2,usecols=range(1,5))‫‫‫‬
>>>print a

>>>b = loadtxt(‘textfile.txt’,skiprows=2,usecols=(1,-2))‫‫‫‬
>>> print b
I/O con Array Numpy
• Per lavorare con molti dati è più conveniente scrivere e leggere in formato
binario.
• Il modo più semplice è usare il modulo cPickle

#Write to File
# a1 and a2 are two arrays
import cPickle
file = open(’tmp.dat’, ’wb’)
file.write(’This is the array a1:\n’)
cPickle.dump(a1, file)
file.write(’Here is another array a2:\n’)
cPickle.dump(a2, file)
file.close()
I/O con Array Numpy

#Read from File


file = open(’tmp.dat’, ’rb’)
file.readline() # swallow the initial comment line
b1 = cPickle.load(file)
file.readline() # swallow next comment line
b2 = cPickle.load(file)
file.close()

Il modulo cPickle garantisce I/O più rapido e un metodo di


immagazzinamento dati a minor costo.
I/O con Array Numpy
• NumPy prevede anche un proprio formato, non portabile, ma piuttosto
efficiente nelle operazioni di I/O

Scrittura e lettura binaria nel formato NumPy

>>> my_data = np.ones((3,3))

>>> np.save(‘myData.npy’,‫‫‬my_data)

>>> r_data = np.load(‘myData.npy’)

In Python è possibile eseguire operazioni di I/O nei formati binari più


diffusi in ambito tecnico scientifico:
• Il supporto al formato HDF5 è disponibile nel modulo ad-hoc
h5py (http://code.google.com/p/h5py/) ed in PyTables
• SciPy dispone del supporto per l’IO nei formati NetCDF, Matlab e
MatrixMarket
Matrix
• Numpy fornisce delle classi standard che ereditano da array e che usano la
sua struttura interna.

• Matrix eredita da ndarray  stessi metodi e attributi


• La classe Matrix ha degli attributi speciali
– .T trasposta
– .H coniugata trasposta
– .I inversa
– .A array bidimensionale

• Matrix definisce oggetti esclusivamente bidimensionali


• Matrix ridefinisce l’operatore * per la moltiplicazione matriciale.
• Gli oggetti Matrix hanno la precedenza rispetto agli array semplici
Matrix
>>>import numpy as np
>>>a=np.arange(16)
>>>a=a.reshape((4,4))
>>>b=2*np.arange(16) array([[ 0, 2, 8, 18],
>>> b=b.reshape((4,4)) [ 32, 50, 72, 98],
[128, 162, 200, 242],
>>>c=a*b #element by element [288, 338, 392, 450]])
>>> ma=np.matrix(a)
matrix([[ 112, 124, 136, 148],
>>> mb=np.matrix(b)
[ 304, 348, 392, 436],
>>> mc=ma*mb #matrixmul [ 496, 572, 648, 724],
[ 688, 796, 904, 1012]])
>>>mmc=ma*b #matrixmul
linalg
• Il modulo Numpy contiene oltre alla definizione dell’object array anche
alcuni moduli.
linalg
Il modulo linalg è un modulo che contiene alcuni algoritmi di algebra
lineare all’interno di numpy.
Contiene funzioni per risolvere sistemi lineari, calcolare autovalori e
autovettori, fattorizzazioni, inverse di matrici, prodotto matriciale.

>>> dir(linalg)
['LinAlgError', 'Tester', '__builtins__', '__doc__', '__file__', '__name__',
'__package__', '__path__', 'bench', 'cholesky', 'cond', 'det', 'eig', 'eigh',
'eigvals', 'eigvalsh', 'info', 'inv', 'lapack_lite', 'linalg', 'lstsq', 'matrix_power',
'matrix_rank', 'norm', 'pinv', 'qr', 'slogdet', 'solve', 'svd', 'tensorinv',
'tensorsolve', 'test']
linalg
Esempio:
A = np.zeros((10,10)) # arrays initialization
x = np.arange(10)/2.0
for i in range(10):
... for j in range(10):
... A[i,j] = 2.0 + float(i+1)/float(j+i+1)
b = np.dot(A, x)
y = np.linalg.solve(A, b) # A*y=b → y=x
# eigenvalues only:
>>> A_eigenvalues = np.linalg.eigvals(A)
# eigenvalues and eigenvectors:
>>> A_eigenvalues, A_eigenvectors = np.linalg.eig(A)
random
random è un altro modulo definito dentro numpy per la generazione di
numeri casuali
dir(random)
['RandomState', 'Tester', '__RandomState_ctor', '__all__', '__builtins__',
'__doc__', '__file__', '__name__', '__package__', '__path__', 'bench', 'beta',
'binomial', 'bytes', 'chisquare', 'dirichlet', 'exponential', 'f', 'gamma',
'geometric', 'get_state', 'gumbel', 'hypergeometric', 'info', 'laplace', 'logistic',
'lognormal', 'logseries', 'mtrand', 'multinomial', 'multivariate_normal',
'negative_binomial', 'noncentral_chisquare', 'noncentral_f', 'normal', 'np',
'pareto', 'permutation', 'poisson', 'power', 'rand', 'randint', 'randn', 'random',
'random_integers', 'random_sample', 'ranf', 'rayleigh', 'sample', 'seed',
'set_state', 'shuffle', 'standard_cauchy', 'standard_exponential',
'standard_gamma', 'standard_normal', 'standard_t', 'test', 'triangular',
'uniform', 'vonmises', 'wald', 'weibull', 'zipf']
random
Generare un array di numeri casuali usando il modulo standard numpy è
inefficiente, meglio usare il modulo numpy.random
>>> np.random.seed(100)
>>> x = np.random.random(4)
array([ 0.89132195, 0.20920212, 0.18532822,
0.10837689])
>>> y = np.random.uniform(1, 1, n) # n uniform
numbers in interval (1,1)

Distribuzione normale
>>> mean = 0.0; stdev = 1.0
>>> u = np.random.normal(mean, stdev, n)
Scipy
Introduzione a SciPy
Introduzione

• Il pacchetto SciPy contiene diversi toolbox dedicati ai problemi più comuni


del calcolo scientifico

• I suoi diversi sotto-moduli corrispondono a diverse applicazioni, come


interpolazione, integrazione, ottimizzazione, elaborazione di immagini,
statistica, funzioni speciali, algebra lineare, …

• SciPy può essere paragonato ad altre librerie standard largamente


utilizzate nel calcolo scientifico come la GSL (GNU Scientific Library per C e
C + +) o i toolbox di Matlab

2
Introduzione

• SciPy è il pacchetto di base per il calcolo scientifico in Python ed è


disegnato per operare in modo efficiente su array NumPy

• Prima di implementare una nuova routine, vale la pena verificare se


l’algoritmo desiderato non sia già implementato in SciPy

• Non di rado, i programmatori non professionisti tendono a “reinventare la


ruota”, una prassi che spesso porta alla produzione di codice buggy, non
ottimizzato, difficile da condividere ed quasi impossibile da mantenere!

• Al contrario le routine di SciPy sono largamente testate e ben ottimizzate


e, quindi, quando possibile, dovrebbe essere utilizzate

3
Warning

• Questo modulo del corso “Python for Computational Science” non vuole
essere una introduzione al calcolo numerico, cosa che è al di la dei nostri
compiti istituzionali.

• Una enumerazione dei diversi sottomoduli e delle funzioni di SciPy


risulterebbe piuttosto noiosa, quindi ci concentreremo su pochi esempi per
fornire un’idea generale su come si usa SciPy per il calcolo tecnico
scientifico

• Per usare efficacemente le funzionalità di SciPy, è consigliabile fare largo


uso delle funzionalità di help dei singoli moduli accessibili dall’interno di
IPython

4
La struttura di SciPy
• Scipy è composto da sotto-moduli specifici per tipologia di applicazione;
ad oggi la lista dei sotto-moduli disponibili è la seguente:

Sub-module Task
scipy.cluster Vector quantization / Kmeans
scipy.constants Physical and mathematical constants
scipy.fftpack Fourier transform
scipy.integrate Integration routines
scipy.interpolate Interpolation
scipy.io Data input and output
scipy.linalg Linear algebra routines
scipy.ndimage n-dimensional image package

5
La struttura di SciPy
Sub-module Task
scipy.odr Orthogonal distance regression
scipy.optimize Optimization
scipy.signal Signal processing
scipy.sparse Sparse matrices
scipy.spatial Spatial data structures and algorithms
scipy.special Any special mathematical functions
scipy.stats Statistics

• Tutti i moduli dipendono da NumPy, ma sono praticamente indipendenti


uno dall’altro; dunque il modo standard di utilizzarli è:

>>> import numpy as np


>>> from numpy import stats #stat sub-mod.
6
File I/O: modulo scipy.io

>>> import numpy as np


>>> from scipy import io
>>> a = np.ones((3,3))
>>> io.savemat('file.mat', {'a': a}) # 2nd arg is a dict
>>> data = io.loadmat('file.mat’, struct_as_record=True)
>>> data[‘a’]
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> from scipy import misc
>>> misc.imread(‘myImage.png’) # e’ anche in matplotlib

7
Funzioni speciali:
il modulo sciyy.special
• Il modulo contiene una vasta scelta di funzioni speciali

• Il suo help è molto bel fatto ed contiene un elenco completo delle funzioni
disponibili, raggruppate per tipologia: è un ottimo riferimento per vedere
cosa è presente nel modulo e come utilizzarlo

• Alcune delle principali (elenco breve e non esaustivo!) funzioni presenti:


– Funzioni di Bessel (scipy.special.jn, …)
– Funzioni ellittiche e funzioni integrali (scipy.special.ellipj, …)
– Funzioni Gamma (scipy.special.gamma, scipy.special.gammaln)
– Error Function (scipy.special.erf)
– Diverse classi di polinomi ortonormali (Legendre, Laguerre, Hermite, …)
8
Linear algebra:
calcolo del determinante

>>> from scipy import linalg


>>> arr = np.array([[1, 2],
... [3, 4]])
>>> linalg.det(arr)
-2.0
>>> arr = np.array([[3, 2],
... [6, 4]])
>>> linalg.det(arr)
0.0
>>> linalg.det(np.ones((3, 4)))
Traceback (most recent call last):
...
ValueError: expected square matrix
9
Linear algebra:
calcolo dell’inversa

>>> from scipy import linalg


>>> arr = np.array([[1, 2],
... [3, 4]])

>>> iarr = linalg.inv(arr)


>>> iarr
array([[-2. , 1. ],
[ 1.5, -0.5]])

>>> np.allclose(np.dot(arr, iarr), np.eye(2))


True

10
Linear algebra:
calcolo dell’inversa

>>> from scipy import linalg


>>> arr = np.array([[3, 2],
... [6, 4]])

>>> iarr = linalg.inv(arr)


Traceback (most recent call last):
...
LinAlgError: singular matrix

11
Linear algebra:
singular-value decomposition
La singular-value decomposition di una matrice A consiste nel calcolo di 2
matrici unitarie U e V ed di un array s di valori singolari (reali e non negativi),
tali che A = USV, in cui S è la matrice diagonale di elementi diagonali s

>>> from scipy import linalg


>>> a = np.arange(9).reshape((3,3)
>>> u, spec, v = linalg.svd(arr)

>>> s = np.diag(spec)
>>> svd = u.dot(s).dot(v)
>>> np.allclose(svd,a)
True NB: se il calcolo non converge,
è generato un LinAlgError

Sono disponibili anche altre decomposizioni standard: QR, LU, Cholesky, Schur 12
Linear algebra:
soluzione di un sistema lineare
Data una matrice A (n,m) ed un vettore b (n), calcolare il vettore x (n) tale
che Ax=b

>>> from scipy import linalg


>>> A = np.array([[1,2],[3,4]])
>>> b = np.array([[5],[6]])
>>> x = linalg.solve(A,b)
>>> x
array([[-4. ],
[ 4.5]])
>>> np.allclose(A.dot(x)-b,np.zeros((2,2)))
True

13
Ottimizzazione e fit:
scipy.optimize
In generale, l’ottimizzazione consiste nella ricerca di una soluzione numerica
a un problema di minimizzazione o nel calcolo degli zeri di una funzione

>>> def f(x):


... return x**2 + 10*np.sin(x)
>>> import pylab as plt
>>> x = np.arange(-10, 10, 0.1)
>>> plt.plot(x, f(x))
>>> plot.show()

La funzione ha un minimo globale intorno a -1 ed


un minimo locale intorno a 4!
14
Ricerca del minimo assoluto

Un metodo generale ed efficiente per la ricerca del minimo della funzione


scelta consiste nel condurre una discesa lungo il gradiente, partendo da un
punto iniziale. L’algoritmo BFGS è una valida soluzione del problema!

>>> from scipy import optimize


>>> optimize.fmin_bfgs(f, 0)
Optimization terminated successfully.
Current function value: -7.945823
Iterations: 5
Function evaluations: 24
Gradient evaluations: 8
array([-1.30644003])

15
Ricerca del minimo assoluto
• Un possibile problema di questo approccio è che, se la funzione presenta
minimi locali, l’algoritmo può giungere ad uno di questi ultimi, invece che
al minimo assoluto, per certe scelte dello starting point della discesa

>>> optimize.fmin_bfgs(f, 3, disp=0)


array([3.83746663])

• Se non si conosce l’intorno in cui cade il minimo assoluto, è possibile


ricorrere a più costosi metodi di ottimizzazione globale

• Per essere certi di aver trovato il minimo assoluto, il metodo più


semplice consiste nell’algoritmo “brute force”: valuta la funzione in tutti i
punti di una data griglia e determina dove la funzione è minima
16
Ricerca del minimo assoluto

>>> grid = (-10, 10, 0.1)


>>> optimize.brute (f, (grid,),disp=True)
Optimization terminated successfully.
Current function value: -7.945823
Iterations: 11
Function evaluations: 22
array([3.83746663])

• In caso di griglia con molti punti, questo metodo può diventare costoso
• La funzione optimize.anneal è una soluzione alternativa al problema,
che usa il metodo del simulated annealing
• Esistono diversi algoritmi, non compresi in SciPy, che risultano più
efficienti per differenti classi di problemi di ottimizzazione globale; in
caso di necessità, segnaliamo OpenOpt, IPOPT, PyGMO e PyEvolve 17
Ricerca degli zeri di una funzione
• La funzione scipy.optimize.fsolve consente di
trovare lo zero di una funzione, ovvero una
soluzione per l’equazione f(x) = 0
• Per semplicità utilizziamo la funzione vista per la
ricerca del minimo assoluto

>>> from scipy import optimize


>>> x_1 = optimize.fsolve(f, 1) #starting point 1
>>> x_2 = optimize.fsolve(f,-3) #starting point -3
>>> x_1
array([0.])
>>> x_2
array([-2.47948183])
18
Fitting di una curva …
Per procurarci il set di dati di cui
trovare il best-fit, partiamo dalla
funzione f degli esempi precedenti,
ed aggiungiamo un po’ di rumore
gaussiano

>>> xd = np.linspace(-10,10,40)
>>> yd = f(xd) + np.random.randn(xd.size)
19
Fitting di una curva
• La forma funzionale con cui fittare il campione evidentemente è ax2 + bsin(x)
• Usiamo il non linear least square fit per determinare il valore ottimale dei
parametri a e b

>>> from scipy import optimize


>>> def f2(x, a, b):
... return a*x**2 + b*np.sin(x)
>>> guess = [2,2]
>>> par, par_cov = optimize.curve_fit(f2,xd,yd,guess)
>>> par
array([ 1.00340129, 10.02551458])
>>> par_cov
array([[ 1.01523991e-05, -2.76706261e-16],
[ -2.76706261e-16, 4.72408931e-02]])

20
Fitting di una curva
Visualizziamo tutto quanto abbiamo trovato sulla funzione f(x) in un
unico plot

21
scipy.stats: istogramma e PDF
• scipy.stats contiene un gran numero di strumenti per descrivere campioni
statistici, per lavorare con distribuzioni di probabilità - continue e discrete - e
per eseguire diverse tipologie di test statistici

• Dato un campione di un processo random, il suo istogramma è un


‘estimatore’ della funzione densità di probabilità del processo.

>>> a = np.random.normal(size=10000) # our sample


>>> eb = np.arange(-4,4.25,0.25) # binning edges
>>> eb.size
33
>>> h= np.histogram(a,bins=eb, normed=True)
>>> h[0].size
32
22
scipy.stats: istogramma e PDF
Costruiamo l’array dei punti medi di ogni bin; poi, usando scipy.stats,
calcoliamo la PDF della normale sugli stessi punti ed infine confrontiamola, su un
grafico, con l’istogramma prodotto

>>> b = 0.5 * (eb[1:] + eb[:-1])


>>> b.size
32
>>> from scipy import stats
>>> my_pdf = stats.norm.pdf(b)

>>> import pylab as pl


>>> pl.plot(b, h[0], label='Histogram')
>>> pl.plot(b, my_pdf, label=‘normal PDF’)

23
scipy.stats: istogramma e PDF

24
Maximum-likelihood fit
Se il campione proviene da un processo random appartenente ad una data
famiglia (i processi gaussiani, nel nostro caso), possiamo usare il maximum-
likelihood fit del nostro campione per stimare i parametri della distribuzione
sottostante

>>> from scipy import stats


>>> a = np.random.normal(size=10000)
>>> stats.norm.fit(a)
(0.0012754030661755386, 0.99565211852482849)
>>> b = np.random.normal(loc=1, scale=3, size=10000)
>>> stats.norm.fit(b)
(0.96615627674018123, 2.9725933382047445)

25
Percentili
• La mediana di un campione è il numero m tale che il 50% degli elementi
del campione sono più piccoli di m ed il restante 50% più grandi di m
• La mediana di un campione è chiamato anche percentile 50 perché il 50
degli elementi del campione ha un valore inferiore
• Il percentile è un estimatore della densità di probabilità cumulativa (CDF)

>>> from scipy import stats


>>> a = np.random.normal(size=1000)
>>> np.median(a)
0.038398647271696326
>>> stats.scoreatpercentile(a,50) # percentile 50
0.038398647271696326
>>> stats.scoreatpercentile(a,95) # percentile 95
1.72982274247371
26
Il T-test
• Un test statistico tipicamente è utile per calcolare un indicatore da
associare alla bontà di un’ipotesi su uno o più campioni di dati
• Dati 2 campioni gaussiani, vogliamo verificare se sono significativamente
differenti: usiamo il cosiddetto T-test

>>> a = np.random.normal(0,1,size=100)
>>> b = np.random.normal(1,1,size=80) # diverso da a
>>> stats.ttest_ind(a,b)
(array(-7.676065328644207), 1.0441890418905031e-12)

>>> c = np.random.normal(0.1,1,size=80) # simile ad a

>>> stats.ttest_ind(a,b)
(array(-0.7067713387921485), 0.48063276502750174)

27
Introduzione a SciPy:
esercizi proposti
Interpolazione:
scipy.interpolate
• Il modulo contiene funzionalità per il fitting di una funzione (1D e 2D) a
partire da dati esperimentali e per la successiva valutazione della
funzione in punti in cui il dato sperimentale è mancante

• Generiamo un “dato sperimentale” prossimo alla funzione seno

>>> t = np.linspace(0,1,10)
>>> noise = 0.1 * (2 * np.random.random(10) -1)
>>> measures = np.sin(2* np.pi * t) + noise

29
Interpolazione 1D lineare e cubica

Eseguiamo un’interpolazione sia lineare che cubica dei dati sperimentali, per
poi confrontarle con i dati misurati in un plot

>>> from scipy.interpolate import interp1d


>>> l_interp = interp1d(t, measures)
>>> c_interp = interp1d(t, measures, kind=‘cubic’)
>>> computed_t = np.linspace(0,1,50)
>>> linear_res = l_interp(computed_t)
>>> cubic_res = c_interp(computed_t)

30
Interpolazione 1D:
plot per confronto con i dati
Costruiamo il plot per il confronto tra le curve calcolate e i dati sperimentali
di partenza

>>> import pylab as pl


>>> pl.plot(t, measures,'o', ms=6, label='misure')
>>> pl.plot(computed_t, linear_res, \
label=’interp. lineare')
>>> pl.plot(computed_t, cubic_res, \
label=’interp. cubica')
>>> pl.grid()
>>> pl.legend()
>>> pl.show()

31
… Interpolazione 1D:
plot per confronto con i dati

32
scipy.integrate:
integrazione numerica
scipy.integrate.quad() è la più generica funzione per l’integrazione
numerica 1D presente in SciPy

>>> from scipy.integrate import quad


>>> result, error = quad(np.sin, 0, 0.5*np.pi)
>>> np.allclose(result, 1) # check del risultato
True
>>> np.allclose(err, 1 - result) # check dell’errore
True

• Oltre al semplice integrale definito, quad sa fare molte altre cose (cfr. help)
• dblquad e tplquad sono usate nel caso 2D e 3D rispettivamente
• In scipy.integrate sono disponibili anche altri schemi d’integrazione
numerica (cfr. fixed_quad, quadrature, romberg) 33
scipy.integrate: ODE

• Il sotto-modulo scipy.integrate contiene anche routine per l’integrazione


di equazioni differenziali ordinarie (ODE)

• In particolare scipy.integrate.odeint() è un integratore general-purpose


di ODE che usa LSODA - Livermore Solver for Ordinary Differential
equations with Automatic method switching for stiff and non-stiff
problems - (cfr. documentazione di ODEPACK per dettagli)

• odeint risolve ODE del prim’ordine della forma

y¢ = f (y,t) con y = [y1 (t), y2 (t),... ,yn (t)]

34
scipy.integrate: ODE

• Come primo esempio, risolviamo l’equazione

dy
= -2y con t Î [0,4] e y(0) =1
dt
• Iniziamo con lo scrivere in Python la funzione che calcola la derivata
rispetto al tempo della funzione y

>>> def calc_derivative(ypos, time, arr_counter):


arr_counter += 1
return -2 * ypos

NB: Abbiamo aggiunto l’ulteriore argomento e_counter, per


illustrare come la funzione sia chiamata più volte ad ogni passo
d’integrazione, fino a che il solutore raggiunge la convergenza

35
scipy.integrate: ODE

>>> counter = np.zeros((1,), dtype=np.uint16)


>>> time_vec = np.linspace(0,4,40)
>>> from scipy.integrate import odeint
>>> yvec, info = odeint(calc_derivative, 1,
... time_vec, args=(counter,),
... full_output=True)

>>> counter # numero di chiamate a calc_derivative


array([129], dtype=uint16)

# n° d’interazioni per i primi 5 time-step


>>> info[‘nfe’][:5]
array([31, 35, 43, 49, 53], dtype=int32)

36
scipy.integrate: ODE

>>> import pylab as pl


>>> pl.plot(time_vec, yvec)

>>> pl.xlabel('Time [s]')

>>> pl.ylabel('y position [m]‘)


>>> show()

37
scipy.integrate: ODE

• Come secondo esempio, usiamo odeint() per risolvere un ODE di secondo


grado, l’equazione di un oscillatore armonico damped:

x¢¢ + 2ew0 x¢ + w x = 0 2
0

k c
in cui w =2
e e=
2mw 0
0
m
con k costante elastica della molla, m massa del oscillatore e c coefficiente
di damping

38
... scipy.integrate: ODE …

Per poter usare odeint() trasformiamo la ODE di secondo grado in un


sistema di ODE di primo grado:

ì dx
ïï = p
dt
í
ï dp = -2ew p- w 2 x
ïî dt 0 0

>>> def dy(y, t, eps, w0):


x, p = y[0], y[1]
dx = p
dp = -2*eps*w0*p – w0**2*x
return [dx, dp]
39
scipy.integrate: ODE

# condizione iniziale
>>> y0 = [1.0, 0.0]
# coordinate temporali in cui trovare la soluzione
>>> t = np.linspace(0,10,1000)
>>> wo = 2*np.pi
>>> from scipy.integrate import odeint
# risolviamo ls ODE per 4 valori diversi di eps
>>> y1 = odeint(dy,y0,t,args=(0.0,w0)) # undamped
>>> y2 = odeint(dy,y0,t,args=(0.2,w0)) # under damped
>>> y3 = odeint(dy,y0,t,args=(1.0,w0)) # critial damping
>>> y4 = odeint(dy,y0,t,args=(5.0,w0)) # over damped

40
scipy.integrate: ODE

>>> from pylab import *


>>> fig, ax = subplots()
>>> ax.plot(t, y1[:,0],'k’,label="undamped",
... linewidth=0.25)

>>> ax.plot(t, y2[:,0], 'r', label="under damped")


>>> ax.plot(t, y3[:,0], 'b', label=r"critical damping")
>>> ax.plot(t, y4[:,0], 'g', label="over damped")
>>> ax.legend()
>>> show()

41
scipy.integrate: ODE

42
Fast Fourier trasform: scipy.fftpack

• scipy.fftpack fornisce essenzialmente le funzioni per utilizzare in Python


la libreria FFTPACK -disponibile su NetLib-, che è un’efficiente e ben testata
libreria per FFT scritta in Fortran.

• Come esempio d’uso delle funzioni FFT di SciPy, calcoliamo la Trasformata


di Fourier di una delle soluzioni del dumped oscillator, appena calcolate

>>> N = len(t); dt = t[1]-t[0]


>>> import scipy.fftpack as fftpack
>>> F = fftpack.fft(y2[:,0])
>>> w = fftpack.fftfreq(N,dt)

43
Fast Fourier trasform: scipy.fftpack

>>> fig, ax = subplots(figsize=(9,3))


>>> ax.plot(w, abs(F));

44
Fast Fourier trasform: scipy.fftpack

• Poiché il segnale è reale, lo spettro è simmetrico; dunque abbiamo


possiamo disegnare il grafico della Trasformata di Fourier calcolata solo in
corrispondenza delle frequenze positive

>>> indices = where(w>0)


>>> w_pos = w[indices]
>>> F_pos = F[indices]
>>> fig, ax = subplots(figsize=(9,3))
>>> ax.plot(w_pos, abs(F_pos))
>>> ax.set_xlim(0, 5);

45
Fast Fourier trasform: scipy.fftpack

Come c’era da aspettarsi, lo spettro presenta un solo picco centrato


intorno ad 1, il valore corrispondente alla frequenza dell’oscillatore del
nostro esempio

46
Matplotlib
Indice
• Modulo Pylab
• Introduzione a Pylab
• Comandi di base
• Figure
• Plot e Subplot
• Axes
• Line2D Properties
• Gestione del testo
• Esempi: diagrammi a barre, pie plot, scatterplot, istogrammi,
meshgrid, contourplot,
Matplotlib: Modulo Pylab
“Matplotlib tries to make easy things easy and hard things possible”

John Hunting

Uno strumento per la grafica bidimensionale è fornito dalla


libreria Matplotlib.

La libreria Matplotlib nasce per emulare in ambiente Python i


comandi grafici di Matlab.

È sviluppata interamente in Python e utilizza il modulo Numpy


per la rappresentazione di grandi array.
Matplotlib: Modulo Pylab
Matplotlib è divisa in tre parti:
• Pylab interface: set di funzioni fornite dal modulo Pylab.
• Matplotlib API
• Backend: grafici per l’output su file e visuali per l’output su
interfacce grafiche.

Contiene diverse funzioni per il calcolo scientifico.

È possibile utilizzare la sintassi LaTex per aggiungere formule nei


grafici.
Matplolib: Modulo Pylab
Quali sono i vantaggi nell’utilizzare matplotlib?

• Usa Python: MATLAB manca di molte proprietà necessarie a


renderlo un linguaggio general purpose
• E’ open source
• E’ cross-platform: Linux, Windows, Mac OS e Sun Solaris
• E’ customizzabile ed estendibile
• Ha un'ottima resa grafica
• Possibilità di generare postscript per includere i grafici in
documenti TeX
• Embeddable in una GUI per lo sviluppo di applicazioni
• Ha una sintassi semplice e leggibile
Introduzione a Pylab
L’interfaccia Pylab costituisce il modo più semplice per lavorare con
Matplotlib.
Le funzioni sono molto simili all’ambiente Matlab.
Esempio
>>>from pylab import *
>>>figure()
>>>show()

La funzione figure() istanzia un oggetto


figura.
La funzione close(n) chiude la finestra n
La funzione show() visualizza tutte le figure
create
Introduzione a Pylab
Le principali entità su cui
lavorare sono:
Figure l’oggetto figure ha
attributi propri
(risoluzione,dimensioni,).
Line2d le linee2d possiedono
diversi marcatori propri,
etc.
Text è possibile modificare e
gestire testo (plain o
math)
Axis per la gestione degli assi
Matplotlib
Matplotlib è disegnata per la programmazione object oriented: si
possono definire oggetti per colours, lines, axes, etc.

Si può adottare anche un approccio funzionale: i plot possono


essere generati usando funzioni, in una interfaccia Matlab-
like.

L’approccio object-oriented è generalmente preferito per plot


non-interattivi (i.e., scripting).

La pylab interface è utile per lavorare interattivamente e


disegnare.
Matplotlib
2 modi per usare Matplotlib:

• Object-oriented way: Il modo Pythonico di lavorare con


Matplotlib. Il modulo pyplot fornisce un'interfaccia alla
libreria matplotlib.

• pylab: Un modulo che unisce Matplotlib e NumPy in un


ambiente simile a MATLAB. Assi e figure sono create
automaticamente dalla funzione di disegno.
Matplolib API
L’approccio OO rende tutto più esplicito e consente la customizzazione dei grafici
Esempio primo.py

import numpy as np Matplotlib API: necessario


import matplotlib.pyplot as plt per embedding in GUI
fig = plt.figure()
ax = fig.add_subplot(111)
t=np.arange(0,5,0.05)
f=2*np.pi*np.sin(2*np.pi*t)
ax.plot(t,f)
ax.set_title('Primo grafico')
ax.grid(True)
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.show()
Matplotlib API
Esempio plots.py
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> x = np.arange(0, 10, 0.1)
>>> y = np.random.randn(len(x))
>>> fig = plt.figure() # instance of the fig obj
>>> ax = fig.add_subplot(111) # instance of the axes obj
>>> l, m = ax.plot(x, y, x, y**2) # returns a tuple of obj
>>> l.set_color('blue')
>>> m.set_color('red')
>>> t = ax.set_title('random numbers')
>>> plt.show()
pyplot vs pylab
pylab
>> from pylab import *
>> t=arange(0,5,0.05)
>> f=2*pi*sin(2*pi*t)
>> plot(t,f)
>> grid()
>> xlabel(‘x’)
>> ylabel(‘y’)
>> title(‘Primo grafico’)
>> show()
pylab mode: preferibile per interactive plotting
Comandi di base di pylab
Esempio onda.py
>>>from numpy import *
>>>from pylab import *
>>>t=arange(0,5,0.05)
>>>f=2*pi*sin(2*pi*t)
>>>plot(t,f)
>>>grid()
>>>xlabel(‘x’)
>>>ylabel(‘y’)
>>>title(‘Primo grafico’)
>>>show()
Il grafico viene visualizzato solo alla chiamata della funzione show().
Per lavorare interattivamente è necessario impostare:
• mode interactive rcParams[‘interactive’]=True
• il tipo di backend rcParams[‘backend’]=‘TkAgg’
Comandi di base
>>>hold(True)
>>>f2=sin(2*pi*t)*exp(-2*t)
>>>plot(t,f2)
>>>legend((‘y=2*pi*sin(2*pi*x)’,‘sin(2*pi*x)*exp(-2*x)’))
Comandi di base
In alternativa :
>>>clf
>>>plot(t,f,'g--o',t,f2,'r:s')
>>>xlabel('x')
>>>ylabel('y')
>>>title('Grafico 1')
SUBPLOT
>>>subplot(211)
>>>plot(t,f)
>>>xlabel(‘x’);ylabel(‘y’) ; title(‘Grafico 1’)
>>>subplot(212)
>>>plot(t,f2)
>>>xlabel(‘x’);ylabel(‘y’) ; title(‘Grafico 2’)
Figure
E’ possibile gestire e creare un numero arbitrario di figure tramite il comando
figure().
E’ possibile gestire i seguenti attributi della figura:
• figsize: dimensione in inches
• facecolor: colore di riempimento
• edgecolor: colore del bordo
• dpi: risoluzione
• frameon: per mantenere il background grigio alla figura.

Per chiudere la figura si possono usare i comandi:


• close(num)
• close(istance)
• close(‘all’)
Figure
x=arange(0,pi,0.01)
y=sin(x)
y2=cos(x)
figure(facecolor='g')
plot(x,y,label='sin(x)')
legend()
figure(figsize=[3,3])
plot(x,y2,label='cos(x)')
legend()
close(1)
close('all')
Plot e Subplot
Il comando plot(line2d , [properties line2d]) è un comando
versatile che consente di creare grafici multilinea specificando
lo stile.
Il comando subplot(nrows,ncol,index) permette di creare
grafici multipli su una griglia con un numero specifico di righe
e di colonne.

subplot(2,3,5)
Plot e Subplot
Creating subplot - pylab

from pylab import *


x = arange (0, 2.0, 0.01)

subplot(2, 1, 1)
plot(x, x ** 2, 'b--')
subplot(2, 1, 2)
plot(x, cos(2*pi*x), 'r.')

subplots_adjust(hspace = 0.5)
show()
Plot e Subplot

Creating subplot - OO
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 8*np.pi,
num=40)
f=plt.figure()
ax=f.add_subplot(2,1,1)
ax.plot(x, np.sin(x))
ax2=f.add_subplot(2,1,2)
ax2.plot(x, np.arctan(x))
f.subplots_adjust(
left=0.13, right=0.97,
top=0.97, bottom=0.10,
wspace=0.2, hspace=0.4)
plt.show()
Axes
L’oggetto axes() permette la gestione degli assi e si comporta in maniera
simile a subplot.
axes() equivale a subplot(111)
axes([left,bottom, width, height]) posiziona e dimensiona il grafico secondo la
lista di parametri passati come argomento.

Alcuni metodi
axis([xmin,xmax,ymin,ymax])
grid()
xticks(location,label)
legend([list_lines],[list_label], loc,
[text_prop])
Axes
Esempio histoaxis.py
import numpy as np
import matplotlib.pyplot as plt
x = np.random.randn(1000)
y = np.random.randn(1000)
axscatter = plt.axes([0.1,0.1,0.65,0.65])
axhistx = plt.axes([0.1,0.77,0.65,0.2])
axhisty = plt.axes([0.77,0.1,0.2,0.65])
axscatter.scatter(x, y)
plt.draw()
binwidth = 0.25
xymax = max( [max(np.fabs(x)),
max(np.fabs(y))] )
lim = ( int(xymax/binwidth) + 1) * binwidth
bins = np.arange(-lim, lim + binwidth, binwidth)
axhistx.hist(x, bins=bins)
plt.draw()
axhisty.hist(y, bins=bins, orientation='horizontal')
plt.draw()
plt.show()
Axes

Esempio doppio.py

import numpy as np
from matplotlib import pyplot as plt
x=[1,2,3,4,5,6,7]
y=[10,20,40,50,10,7,10]
y2=[4,10,3,4,3,10,10]

f=plt.figure()
ax=f.add_axes([0.1,0.55,0.7,0.4])
l1,=ax.plot(x,y,'r--',marker='o')
l2,=ax.plot(x,y2,marker='s',color='green',linestyle='-.')
ax.set_xticks(x)
ax.set_xticklabels(['Jan','Feb','Mar','Apr','May','Jun',
'Jul'])
ax.legend([l1,l2],['sun','rain'])
bx=ax.twiny()
bx.set_xticks(x)

ax2=f.add_axes([0.1,0.1,0.7,0.4])
ax2.plot(np.arange(10),np.arange(10),label='small')
ax2.legend(loc=2)
by=ax2.twinx()
by.plot(np.arange(10),np.exp(np.arange(10)),'r',label='big')
by.legend()
plt.show()
Line2D Properties
L’oggetto linea ha diversi attributi: è possibile modificare le dimensioni, lo stile, il
colore etc. La funzione:
setp(*args, **kwargs)
permette di cambiare tali attributi.
In alternativa è possibile modificare gli attributi tramite i metodi dell’oggetto line2D.
Tra gli attributi ricordiamo:
• color ‘b’, ‘r’, ‘g’, ‘y’, ‘ k’, ‘w’’, ‘c’, ’m’
• linewidth float
• linestyle ‘’, ’-’, ’- -’, ’:’, ‘.-’
• label stringa
• marker ‘.’, ‘o’, ‘D’, ‘^’, ‘s’, ‘*’, ‘+’, ‘h’
• markersize float
• markerfacecolor color
Line2D Properties

Creating subplot-- pylab

x=np.arange(0,np.pi,0.1)
np.plot(x,sin(x),marker='o',color='r',
markerfacecolor='b',label='sin(x)')
np.legend()
Line2D Properties
Creating subplot-- OO
Esempio sale.py

import numpy as np
from matplotlib import pyplot as plt

x=np.arange(0,100,10)
y=2.0*np.sqrt(x)
f=plt.figure()
ax=f.add_subplot(111)
line,=ax.plot(x,y)
line.set_color('r')
line.set_linestyle('--')
line.set_marker('s')
plt.setp(line,markeredgecolor='green',
markerfacecolor='b',markeredgewidth=3)
line.set_markersize(15)
plt.show()
Line2D Properties
Creating Multi-line plot
Creating subplot-- pylab

t=np.arange(0,5,0.05)
f=2*np.pi*np.sin(2*np.pi*t)
f2=np.sin(2*np.pi*t)*np.exp(-2*t)
plt.plot(t,f,'g--o',t,f2,'r:s‘)
hold(True)
f3=2*np.pi*np.sin(2*pi*t)*np.cos(2*pi*t)
plt.plot(t,f3,'c-.D',label='f3')
plt.legend(('f1','f2‘,’f3’))
plt.show()
Line2D Properties
Creating Multi-line plot
Creating subplot-- OO

Esempio crescedecresce.py

import numpy as np
from matplotlib import pyplot as plt
x=np.arange(0,100,10)
y1=2.0*np.sqrt(x);
y2=3.0*x**(1.0/3.0)
y3=4.0*x+3.0*x**2
y4=5.0*x-2.0*x**2
f=plt.figure()
ax=f.add_subplot(111)
line1,=ax.plot(x,y1,'r--')
line2,=ax.plot(x,y2,'b-.')
line3,line4=ax.plot(x,y3,x,y4)
line3.set_color('g')
line4.set_color('y')
ax.legend([line2,line3,line4],['line2','line3','line4‘])
plt.show()
Gestione del testo

Pylab permette di gestire stringhe di testo all’interno di grafici.


xlabel (s, *args, **kwargs)
ylabel (s, *args, **kwargs)
title (s, *args, **kwargs)
annotate (s, xy, xytext=None,
xycoords='data',
textcoords='data',
arrowprops=None,**props)
text (x, y, s, fontdict=None,
**kwargs)
Gestione del testo

Inoltre Pylab è in grado di inglobare espressioni matematiche in


espressioni di testo utilizzando la sintassi LaTex.

Per esempio la sintassi: xlabel(r’$y_i=2\pi \sin(2\pi x)$’)


equivale a

E’ necessario inoltre imporre: rcParams(text.usetex)=True


Text Properties
L’oggetto testo possiede le seguenti proprietà:
• Fontsize: xx-small, x-small, small, medium, large, x-large,
xx-large
• Fontstyle: normal, italic, oblique
• Color
• Rotation: degree , ‘vertical’, ’horizontal’
• Verticalalignment: ‘top’, ‘center’, ‘bottom’
• Horizontalalagnment: ‘left’, ‘center’, right’
Text Properties
Gli attributi possono essere modificati in tre modi:
• Tramite keyword arguments, tramite la funzione setp, tramite i metodi
dell’oggetto testo:

>>>plt.xlabel(‘ciao’, color =‘r’, fontsize=‘large’) #keyword arguments


>>>l=plt.ylabel(‘asse y’)
>>>plt.setp(l,rotation=45) #setp()
>>>l.set_color(‘r’) #object method
Text

x=[9,10,13,12,11,10,9,8,45,11,12,10,9,
11,10,13,9]
plt.plot(x,label='myfunc')
plt.legend()
plt.title('Mytitle')
plt.ylabel('y',fontsize='medium',color='r')
plt.xlabel('x',fontsize='x-large',color='b',position=(0.3,1))
plt.text(4,20,'mytext', color='g',fontsize='medium')
plt.annotate('annotate',xy=(8,45),xytext=(10,
35),arrowprops=dict(facecolor='black',shrink=0.05))
Images File

Ci sono diversi modi per usare matplotlib:


• Lavoro interattivo tramite shell python (meglio IPython).
• Attraverso degli script di processamento e generazione di file
di immagini.
• Embedding in una graphical user interface, per consentire
all’utente di interagire con i dati visualizzati.
Images File

La visualizzazione del plot è time-consuming, specialmente per


plot multipli e complessi.
I plot possono essere salvati senza essere visualizzati tramite la
funzione savefig() :
x = np.arange(0,10,0.1)
plt.plot(x, x ** 2)
plt.savefig(‘C:/myplot.png’)
Diagrammi a barre
Come creare un diagramma a barre:
bar(left, height)
Esempio barre.py
n_day1=[7,10,15,17,17,10,5,3,6,15,18,8]
n_day2=[5,6,6,12,13,15,15,18,16,13,10,6]
m=['Jan','Feb','Mar','Apr','May','Jun‘,
'Jul','Aug','Sept','Oct','Nov','Dec']
width=0.2; i=np.arange(len(n_day1))
r1=plt.bar(i, n_day1,width, color='r',linewidth=1)
r2=plt.bar(i+width,n_day2,width,color='b',linewidth=1)
plt.xticks(i+width/2,m)
plt.xlabel('Month'); ylabel('Rain Days'); title('Comparison')
plt.legend((r1[0],r2[0]),('City1','City2'),loc=0,labelspacing=0.06)
plt.show()
Torta
Oppure con gli stessi dati come creare una torta:
pie(x)
Esempio torta.py
plt.subplot(211)
plt.pie(n_day1,labels=m,
explode=[0,0,0,0.1,0.1,0,0,0,0,0,0.1,0],
shadow=True)
plt.title('City1')
plt.subplot(212)
plt.pie(n_day2,labels=m,
explode=[0,0,0,0,0,0,0,0.1,0.1,0,0,0],
shadow=True)
plt.title('City2')
Meshgrid
• Come costruire una griglia bidimensionale?
• Data una griglia (xi,yi) vogliamo calcolare per ciascun punto della griglia il
valore della funzione f(xi,yi)
>>> x=np.arange(4)
>>> y=np.arange(4)
>>> def f(x,y):
return x**2+y
>>> x
array([0, 1, 2, 3])
>>> y
array([0, 1, 2, 3])
>>> f(x,y)
WRONG!!
array([ 0, 2, 6, 12])
Meshgrid
xx,yy=np.meshgrid(x,y)
>>> xx
3
array([[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3], 2
[0, 1, 2, 3]])
>>> yy 1
array([[0, 0, 0, 0],
[1, 1, 1, 1], 0
[2, 2, 2, 2], 1 2 3
[3, 3, 3, 3]])
>>> f(xx,yy)
array([[ 0, 1, 4, 9],
[ 1, 2, 5, 10],
OK!!
[ 2, 3, 6, 11],
[ 3, 4, 7, 12]])
Contour plot
import numpy as np contourf(*args, **kwargs)
from matplotlib import pyplot as plt contour(*args,**kwargs)
from matplotlib import mlab as ml meshgrid(x,y)

delta = 0.5
x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = ml.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = ml.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 10
levels = np.arange(-2.0, 1.601, 0.4)
Contour plot

plt.figure(facecolor="w")
plt.subplot(221)
plt.imshow(Z,origin= 'lower')
plt.subplot(222,axisbg="w")
l= plt.contourf(Z,levels,origin='lower')
plt.colorbar(l)
plt.subplot(223,axisbg="w")
l= plt.contour(Z, levels,origin= 'lower',linewidths=2)
plt.clabel(l,inline=1, fmt='%1.1f',fonsize=14)
plt.show()
Output

Matplotlib supporta diversi backend grafici. Possiamo dividere la tipologia di


backend in due categorie:
• User interface backend: per l’assemblaggio in GUI. In Python esistono
diverse librerie per la costruzione di interfaccie grafiche tra cui Tkinter,
PyQt, pygtk che vengono supportate da matplotlib.
• Hardcopy backend: per la stampa su file. Vengono supportati i seguenti
formati *.jpg, *png, *svg, *pdf, *rgba.
Mixed Language
Programming
Indice

• Timing your code


• Numexpr
• Python vs Fortran
• C-API
• F2PY
• SWIG
• Cython
• Benchmark
Python Optimization strategies

• Per migliorare le prestazioni di un codice Python esistono diverse


strategie:

– Sfruttare la libreria Numpy e vettorizzare il codice


– Sfruttare pacchetti ottimizzati per task specifici
– Cython,Swig,f2py e integrazione con linguaggi low level
– Parellelizzazione
– GPU porting
Python Optimization strategies

STEP 0: Prima di ottimizzare individuare


gli hot-spot del codice
Timing your code
Con il modulo timeit è possibile misurare il tempo di esecuzione di una
funzione o di una espressione.
E’ particolarmente adatto per test molto piccoli e su test particolarmente
veloci (micro-sec) e può essere richiamato da riga di comando.

Esempio:
import timeit
def test_func():
a=[]
for el in xrange(1000000): a.append(el)
if __name__==‘__main__’:
print timeit.Timer(‘for el in range(10000000):pass’).timeit(1), ‘s’
print timeit.Timer(‘for el in xarange(10000000):pass’).timeit(1), ‘s’
print timeit.Timer(‘test_func()’, ‘from __main__ import
test_func()’).timeit() ,’s’
Timing your code

0.334960419911 s
0.947776416355 s
0.439117697007 s

Oppure da linea di comando:

python -m timeit -n 1 "for el in range(1000000):pass“


python -m timeit -n 1 ”for el in xrange(1000000):pass”
python -m timeit -n 1 -s “import timeit” -s “import mymodule”
“mymodule.test_func”
Timing your code
Il modulo cProfile è lo strumento standard di Python per il profiling di un
codice.

import cProfile
def test():
lista=[]
for el in xrange(1000000):
lista.append[el]

if __name__==‘__main__’:
cProfile.run(‘test_func()’)
Timing your code

OUTPUT
1000003 function calls in 1.383 CPU seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:


1 0.015 0.015 1.383 1.383 <string>:1(<module>)
1 0.841 0.841 1.368 1.368 p.py:5(test_func)
1000000 0.526 0.000 0.526 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {method 'disable‘, '_lsprof.Profiler' objects}
Timing your code

In maniera analoga ai metodi del modulo timeit, il profiling di un codice o di


una parte di esso può essere effettuato direttamente da linea di comando.

python -m cProfile mymodule.py

Specificando il metodo di ordinamento delle informazioni e reindirizzando


l’output:

python –m cProfile –s ‘cumulative’ –o ‘profile_file.txt’ mymodule.py


Python and C/C++ or Fortran
Python ??
Ease of Use
(YOUR TIME)
C/C++
Fortran

Speed
(CPU TIME)

C/C++ & Fortran: Python:

• High performance • Low performance


wrapper
• Basso livello • Alto livello
(comm. layer)
•Tipizzazione statica • Tipizzazione
dinamica
Python vs Fortran
• Python
• Si pensa sia lento
• E’ tutto ad oggetti
• Lento per il puro calcolo scientifico
• Numpy e altro per ottimizzare

• Fortran
• Pensato per il calcolo scientifico
• E’ veloce
• Le performance sono il suo punto di forza
Python vs Fortran

• Task: Trovare le coordinate comuni in un due file di dati di questo tipo:

x y z
-0.930730079412 -2.5837626746 -1.54168368133
0.242648096556 -0.0356080560781 1.64592869632
1.08469025095 -0.0499861074276 0.850168034143
0.904693997768 0.331774955996 -0.435462229263
-0.30666600039 0.502104683611 -0.147720257532
0.67879008187 0.594205526229 -2.76854972969
0.346956345057 0.64044334422 -0.397530419261
-0.415357107828 0.431902374758 -0.96024517464
-0.0423741085785 0.97632569202 1.13665523717
-0.0890995975459 -0.0170642871372 -0.209950709345
Python vs Fortran
import sys

def read_file_short(file_name):
with open(file_name) as fobj:
next(fobj)
return (tuple(float(entry) for entry in line.split()) for line in fobj)

def find_intersection(file_name1, file_name2):


data1=set(read_file(file_name1))
data2=set(read_file(file_name2))
return data1.intersection(data2)

if __name__=='__main__':
if (len(sys.argv)>1):
dim=sys.argv[1]
print find_intersection('value_'+dim+'_0.txt', 'value_'+dim+'_1.txt')
Python vs Fortran
Lettura dei dati
subroutine read_data(file_name, data_array, line, row)
implicit none
integer,intent(in)::line
integer,intent(in)::row
integer::status
integer::l
double precision, dimension(line,row)::data_array
character(len=20),intent(in)::file_name
Character(len=100)::dummy
open(unit=99,file=file_name,status='old',action='read')
read(99,*,iostat=status) dummy
do l=1,line
read(99,*,iostat=status) data_array(l,1), data_array(l,2),
data_array(l,3)
if (status /=0) exit
enddo
close(99)
end subroutine read_data
Python vs Fortran
Funzione di intersezione
subroutine find_intersection(data_array1,data_array2,line1,line2,row)
implicit none
integer, intent(in)::line1,line2,row
integer::status,counter=0,l1,l2,r,matches
double precision, intent(in),dimension(line1,row)::data_array1
double precision, intent(in),dimension(line2,row)::data_array2
double precision, allocatable, dimension(:,:):: found_temp
double precision, allocatable, dimension(:,:):: found
allocate(found_temp(min(line1,line2),row),stat=status)
do l1=1,line1
do l2=1,line2
matches=0
do r=1,row
if(data_array1(l1,r)==data_array2(l2,r)) then
matches=matches+1
endif
enddo
if (matches==3) then
counter=counter+1
do r=1,row
found_temp(counter,row) =data_array1(l1,r)
enddo
endif
enddo
Python vs Fortran

enddo
allocate (found(counter,row),stat=status)
if(status /=0) then
write(*,*),'Error allocating'
end if
do l1=1,counter
do r=1,row
found(l1,r)=found_temp(l1,r)
end do
end do
write(*,*) counter
end subroutine find_intersection
Python vs Fortran
Programma chiamante
program common_coord
implicit none
integer::number_of_line1=0
integer::number_of_line2=0
integer::status
character(len=100)::dummy
double precision,allocatable, dimension(:,:) :: data_array1
double precision,allocatable, dimension(:,:) :: data_array2
character(len=100)::dimension
character(len=20) :: file_name1 = "value_10000_0.txt"
character(len=20) :: file_name2 = "value_10000_1.txt"
call count_line(file_name1,number_of_line1)
call count_line(file_name2,number_of_line2)
allocate(data_array1(number_of_line1,3),stat=status)
call read_data(file_name1,data_array1, number_of_line1,3)
allocate(data_array2(number_of_line2,3),stat=status)
if(status /=0) then
write(*,*) 'Error allocating 2.'
end if
call read_data(file_name2,data_array2,number_of_line2,3)
call find_intersection(data_array1,data_array2,number_of_line1,number_of_line2,3)
end program common_coord
Python vs Fortran

• Python è più flessibile


• Python è più compatto
• 8 righe vs 107
• Python dispone di contenitori dati molto flessibili

Dimension Python (s) Fortran (s)


1000 0,092 0,051
10000 0,189 1,984
100000 0,876 238
Benefici
L’integrazione è interessante in almeno 2 contesti:

• Migrazione di codice lento: scrittura di un nuovo codice in Python


migrando la parte di calcolo intensivo verso linguaggi HPC.
• Accesso a codici già esistenti: utilizzo di libreria e codici validati scritti in
linguaggi HPC, direttamente in Python

In entrambi i casi Python è utilizzato per task non numerici, traendo benefici
dalla semplicità nella gestione di:
• I/O
• interfacce
• gestione dell’applicazione
• reporting e post processing
• GUI
Strategie: C-API
Chiamare funzioni scritte in C/C++ e F77 in Python non è banale:

compilati interpretati
fortemente tipizzati VS debolmente tipizzati

La maniera nativa per scrivere un modulo Python in C consiste nell’usare le C-API di Python.

ESEMPIO:

extern char * hello(char* a); // hello è presente in un modulo M1


from M1 import hello
#chiamata
c= hello(“Ciao Mondo”)
deve esistere una funzione wrapper (scritta in C, che effettua la traslazione dei tipi di dato e
che converta il risultato ritornato dalla funzione in un oggetto Python)
Strategie: C-API
In questo caso il wrapper è strutturato come segue:
char * hello(char * what)
static PyObject * hello_wrapper(PyObject * self, PyObject * args)
{
char * input;
char * result;
PyObject * ret;
// parse arguments
if (!PyArg_ParseTuple(args, "s", &input))
{ return NULL; }
// run the actual function
result = hello(input);
// build the resulting string into a Python object.
ret = PyString_FromString(result);
free(result);
return ret;
}
//Register function to module
static PyMethodDef HelloMethods[] = { { "hello", hello_wrapper, METH_VARARGS, "Say hello" }, {
NULL, NULL, 0, NULL } };
//Init method
DL_EXPORT(void) inithello(void) { Py_InitModule("hello", HelloMethods); }
Strategie: C-API
Il wrapper va compilato (C-compiler) e il codice oggetto va linkato al codice oggetto
del modulo M1 (scritto in C che contiene la definizione della funzione hello che
vogliamo utilizzare).

Viene generata una libreria (extension module) che può essere importata in Python
con un import classico.

A questo livello in Python è IMPOSSIBILE distinguere tra una modulo pure python ed
una extension module.

Possono essere scelti principalmente 2 approcci:


• utilizzo delle Python C API (Application Programming Interface)
• utilizzo di strumenti automatici
Python C-API: costi

L’utilizzo delle Python C API comporta una grande molo di lavoro:

• Ogni funzione C che si vuole utilizzare DEVE ESSERE FORNITA DI WRAPPER


• Error-prone
• Conoscenza dettagliata dell’interfaccia C a Python.

Strumenti automatici:
• SWIG (Semplified Wrapper Interface Generator)
• F2PY
• Cython
• Ctypes
• …
F2PY
F2PY è un tool che permette di creare interfacce Python a funzioni scritte in Fortran e C.
F2PY è nativamente presente nella libreria Numpy e non necessità di installazione a parte se
Numpy è già presente.
ESEMPIO:
#!/usr/bin/env python
"“”hw: modulo in python""“
import math

def hw1(r1, r2):


s = math.sin(r1 + r2)
return s

def hw2(r1, r2):


s = math.sin(r1 + r2)
print 'Hello, World! sin(%g+%g)=%g' % (r1, r2, s)
F2PY
#!/usr/bin/env python
"""uso del modulo hw da python"""
import sys
from hw import hw1, hw2
try:
r1 = float(sys.argv[1]); r2 = float(sys.argv[2])
except IndexError:
print 'Usage:', sys.argv[0], 'r1 r2'; sys.exit(1)

print 'hw1, result:', hw1(r1, r2)


print 'hw2, result: ',hw2(r1, r2)
F2PY: Fortran Automatic Wrapper
PROGRAM hwtest
REAL *8 r1, r2 ,s
r1 = 1.0
r2 = 0.0
s = hw1(r1, r2)
WRITE(*,*) 'hw1, result:',s
WRITE(*,*) 'hw2, result:'
CALL hw2(r1, r2)
END
SUBROUTINE hw2(r1, r2)
REAL *8 r1, r2, s
s = sin(r1 + r2)
WRITE(*,1000) 'Hello, World! sin(',r1+r2,')=',s
1000 format(A,F6.3,A,F8.6)
RETURN
END

REAL*8 FUNCTION hw1(r1, r2)


REAL*8 r1, r2
hw1 = sin(r1 + r2)
RETURN
END
F2PY
Per generare il wrapper:

f2py –m hw --fcompiler=gfortran –c hw.f90

-m specifica il nome del extension module che vogliamo creare


-c specifica che vogliamo compilare e linkare il modulo
--fcompiler indica il compilatore da utilizzare

In output abbiamo l’extension module sotto la forma di un hw.so (.dll in win32; .dylib
in Mac OS X)
F2PY
Possiamo testare che il modulo sia stato generato con successo :

>>>import hw
>>> hw.hw1(1,2)
0.14112000805986721
>>> hw.hw2(1,2)
Hello, World! sin( 3.000)=0.141120

Per moduli molto grandi è possibile rendere wrappate solo alcune funzioni, con la sintassi:

f2py –m hw –c --fcompiler=gfortran hw.f only: hw1 hw2:

F2PY è stato inizialmente progettato per interfacciare funzioni fortran a Python ma può essere
utilizzato anche per creare wrapper di funzioni scritte in C.
F2PY
• Possiamo migliorare l’interfaccia prodotta e renderla più vicina allo ‘stile
Python’
$ f2py -h mult.pyf -m mult mult.f90
(mult.pyf)
python module mult python module mult
interface interface
subroutine fmult(a,b,c,n) subroutine fmult(a,b,c,n)
real*8 dimension(*) :: a real*8 dimension(n) :: a
real*8 dimension(*) :: b real*8 dimension(n) :: b
real*8 dimension(*) :: c real*8 intent(out), dimension
integer :: n (n) :: c
end subroutine fmult integer intent(hide), depend(
end interface a) ::
end python module mult n=len(a)
end subroutine fmult
Generato in automatico end interface
Python call: end python module mult
fmult(a,b,c,len(a))
Intervento manuale
#a,b Input
f2py -c mult.pyf mult.f90
#c Output
F2PY
>>> import numpy as np
>>> import mult
>>> a = np.array([1, 3, 4])
>>> b = np.array([2, 5, 1.5])
>>> c = mult.fmult(a, b)
>>> f = mult.fmult([3,4], [1.2,1.4])
>>> f
array([ 3.6, 5.6])
F2PY
Oppure inserendo le direttive come commento nei sorgenti
(mult2.f90)
subroutine fmult(a,b,c,n)
implicit none
integer, parameter :: dim=1000
real*8 :: a(n)
real*8 :: b(n)
real*8 :: c(n)
integer :: n, i
!f2py intent(hide), depend(a) :: n=len(a)
!f2py real*8 :: a(n)
!f2py real*8 :: b(n)
!f2py real*8, intent(out) :: c(n)
do i =1,n
c(i) = a(i) * b(i)
enddo
end
Swig
SWIG (Simplified Wrapper and Interface Generator) è un tool:

• permette di creare interfacce di codice scritto in C/C++ per linguaggi


interpretati (Perl, Python,Tuby, Tcl)

• E’ pensato per lavorare con codice C/C++ già esistente, usando solo una
interfaccia di scripting.

• Deve essere installato a parte: http://www.swig.org/index.php

• Supporta le strutture C e le classi C++

• E’ indipendente dal linguaggio

• Supporta una varietà di opzioni di customizzazione.


Swig

• Per generare un wrapper è necessario creare un file di ‘interfaccia’ per


SWIG.
• La sintassi del file di interfaccia consiste in un mix delle direttive SWIG
(identificate da %) , da istruzioni per il preprocessore C (identificate da #),
da codice C e da commenti

%module: definisce il nome dell’extension module


%{…}% blocco utilizzato per inserire codice C necessario per rendere corretta
la compilazione (header files, dichiarazione di funzioni e dipendenze)

• infine vengono dichiare le funzioni che devono essere wrappate


nell’interfaccia.
Esempio
/* file: hw.c */
#include <stdlib.h> /* need atof */
#include <stdio.h>
#include <math.h>

double hw1(double r1, double r2)


{
double s;
s = sin(r1 + r2);
return s;
}
void hw2(double r1, double r2)
{
double s;
s = sin(r1 + r2);
printf("Hello, World! sin(%g+%g)=%g\n", r1, r2, s);
}
Esempio
/* file: hw.c */
int main(int argc, char* argv[])
{
double r1, r2, s;
if (argc < 3) { /* need two command-line arguments */
printf("Usage: %s r1 r2\n", argv[0]); exit(1);
}
r1 = atof(argv[1]); r2 = atof(argv[2]);
printf("hw1, result: %g\n", hw1(r1, r2));
printf("hw2, result: ");
hw2(r1, r2);
return 0;
}
Esempio

/* file: hw.h*/
#ifndef HW_H
#define HW_H
extern double hw1(double r1, double r2);
extern void hw2(double r1, double r2);
#endif
Esempio

/* file: hw.i */
%module hw
%{
/* include C header files necessary to compile the interface */
#include "hw.h"
%}

double hw1(double r1, double r2);


void hw2(double r1, double r2);
Swig steps
1. la generazione del codice wrappato avviene tramite la chiamata di swing:
swig –python –I/path_to_hw.h .. hw.i
L’output del processo è un file che contiene il C wrapper code (.c) e un file hw.py
che costituisce l’interfaccia all’extension module

2. Il wrapper C code va compilato insieme al C source code che contiene le


implementazioni delle funzioni e linkato ad esso in un object file (_hw.so) che
costituisce l’extension module.
Creazione dell’object file compilato con l’opzione -fPIC
gcc -fPIC –I/data/apps/bin/epd-5.0.0-rh5-
x86_64/include/python2.5/ -I/data/apps/bin/epd-5.0.0-
rh5-x86_64/lib/python2.5/config -c ../hw.c hw_wrap.c
Creazione della shared lib
gcc -shared -o _hw.so hw.o hw_wrap.o
Swig
• In alternativa è possibile automatizzare le fasi di compilazione sfruttando il modulo
distutils.
• Creare un file di setup.py:
import commands, os
from distutils.core import setup, Extension
name='hw'
version=1.0
swig_cmd = 'swig -python %s.i' %name
print 'Running Swig: ', swig_cmd
failure, output =commands.getstatusoutput(swig_cmd)
module1 = Extension('_hw',
sources = ['hw.c','hw_wrap.c'])
setup (name = 'PackageName',
version = '1.0',
description = 'This is a demo package',
ext_modules = [module1])
Eseguire il comando:
python setup.py build
Example
Un esempio di utilizzo reale di swig:
• pyMagma
Example
Example
Cython
Il progetto Cython affronta il problema delle performance del codice Python per
mezzo di un compilatore di codice sorgente che traduce il codice Python in codice
C equivalente.
Cython può essere utilizzato per :
•L’ estensione dell'interprete Python con moduli binari veloci
•L’interfacciamento di codice Python con librerie C esterne

Cython può compilare la maggior parte dei sorgenti Python

Il codice C generato presenta miglioramenti di velocità prevalentemente a causa


tipizzazione statica (opzionale) che può essere applicata, nel sorgente Cython, sia
alle variabili C che a quelle Python

Pertanto in Cython è possibile assegnare la semantica C a parti del proprio codice,


in modo che queste possano essere tradotte in codice C estremamente veloce
Cython
Alcune regole di sintassi di base:
• Definizione di variabili: lo statement cdef permette di definire delle
variabili C:
cdef int a
cdef struct my_struct:
int a;
double b;
•Definizione di funzioni: ci sono tre tipi di funzioni in Cython, funzioni Python
definite con lo statement def che ricevono/restituiscono Python objects,
funzioni C definite con lo statement cdef che ricevono/restituiscono Python
objects o dati C, funzioni definite con lo cpdef
def my_function(a, b) #Python function
cdef double my_function2(int a, double b) #C function
cdef my_function3(a,b) #C function with Python object
cpdef my_function(a,b)
Cython
def distance(x, y): Una def function è una funzione Python
return np.sum((x-y)**2) utilizzabile sia in Python che in Cython

cdef float distance(float *x, float *y, int n):


cdef:
int i
float d = 0.0
for i in range(n): Una cdef function è una funzione tradotta
d += (x[i] – y[i])**2 in C ed utilizzabile solo all’interno del file
return d Cython (non direttamente da Python)

cpdef float distance(float[:] x, float[:] y):


cdef int i
cdef int n = x.shape[0]
cdef float d = 0.0 Una cpdef function è una funzione
for i in range(n): tradotta in C ed è utilizzabile sia all’interno
d += (x[i] – y[i])**2 del file Cython sia nel codice Python che
return d importa il modulo che la contiene
45
Cython workflow per velocizzare
Python
Python
def fib(n): Codice C generato da Cython
a,b = 1,1 static PyObject
*__pyx_pf_5cyfib_cyfib(PyObject
*__pyx_self, int __pyx_v_n) {
for i in range(n): int __pyx_v_a; int __pyx_v_b;
a, b = a+b, a PyObject *__pyx_r = NULL;
PyObject *__pyx_t_5 = NULL;
return a const char *__pyx_filename =
NULL;
...
for (__pyx_t_1=0;
__pyx_t_1<__pyx_t_2;
__pyx_t_1+=1) {
__pyx_v_i = __pyx_t_1;
Cython __pyx_t_3 = (__pyx_v_a +
__pyx_v_b);
def fib(int n): __pyx_t_4 = __pyx_v_a;
__pyx_v_a = __pyx_t_3;
cdef int i, a, b __pyx_v_b = __pyx_t_4;
a,b = 1,1 }
...
for i in }
range(n):
a, b = a+b,
a
return a
Cython pyx file
Generato da Cython
Cython
C Extension File
source file
cython fib.c
fib.pyx
Scritto dall’utente

compile

Python Extension
Module
fib.so
Wrapping di codice scritto in C/C++
C/C++
Codice C generato da Cython
int fact(int n) {
static PyObject
if (n <= 1) *__pyx_pf_5cyfib_cyfib(PyObject
*__pyx_self, int __pyx_v_n) {
return 1; int __pyx_v_a; int __pyx_v_b;
PyObject *__pyx_r = NULL; PyObject
return n * fact(n-1); *__pyx_t_5 = NULL;
} const char *__pyx_filename = NULL;
...
for (__pyx_t_1=0;
__pyx_t_1<__pyx_t_2; __pyx_t_1+=1)
{
__pyx_v_i = __pyx_t_1;
__pyx_t_3 = (__pyx_v_a +
__pyx_v_b);
__pyx_t_4 = __pyx_v_a;
Cython __pyx_v_a = __pyx_t_3;
__pyx_v_b = __pyx_t_4;
cdef extern from “fact.h”: }
int _fact “fact”(int) ...
}

def fact(int n):


return _fact(n)
Cpython pyx file
Generato da Cython
Cython
C Extension File
source file
cython fib.c
fib.pyx
Scritto dall’utente
compile

Library Files (if wrapping)


Python Extension
*.h files *.c files
Module
compile fib.so
Cython
•Uno dei principali utilizzi di Cython è per il wrapping di librerie C già esistenti. Cython può
essere utilizzato come ponte per consentire a Python di chiamare funzioni C.
Il file len_extern.pyx
# Prima di tutti includiamo l’header file di cui abbiamo bisogno
cdef extern from "string.h":
# Descriviamo l’interfaccia delle funzioni da utilizzare
int strlen(char *c)

# La funzione strlen può essere usata in Cython, ma non in Python.


def get_len(char *message):
return strlen(message)

Importiamo il modulo in Python ed usiamo la funzione get_len


>>> import len_extern
>>> len_extern.strlen
Traceback (most recent call last):
AttributeError: 'module' object has no attribute 'strlen'
>>> len_extern.get_len(”ciao!")
5
Cython

Source file e compilazione:


• I codici sorgenti devono essere salvati in un file con estensione .pyx

• Per la generazione dell’extension module è possibile operare in due modi:

- Compilare i .pyx files con il Cython compiler, ottenendo dei .c file sorgenti
e compilare il codice C in maniera appropriata, per generare un
eseguibile, con i compilatori disponibili sulla propria piattaforma di lavoro.

- Utilizzare il modulo Cython.Distutils scrivendo un opportuno file di


setup.py.
Esempio
//mylib.h
#include<math.h>
#include<stdio.h>
#include<stdlib.h>

double function(double x);


double integrate_trap(double a, double b, int N);

//mylib.c
#include "mylib.h"

double function(double a){


return pow(a,2);
}

double integrate_trap(double a, double b, int N){


double s=0.0;
double dx=fabs((b-a))/N;
int i=0;
double xa=a;
for (i=0;i<N;i++){
s+=((function(xa)+function(xa+dx))*dx)/2;
xa=xa+dx;}
return s;}
Esempio
#Definizione delle librerie C:
cdef extern from "mylib.h":
double function(double a)
double integrate_trap(double a, double b, int N)

#Definizione della funzione Python


def integrate_rect(a,b,N):
s=0.0;
dx=float(b-a)/N;
for i in range(N):
s+=function(a+i*dx)
return s*dx

print 'Result ', s


Esempio
cdef double s=0.0
print 'Compute integration of function x^2'
print 'Between interval a, b with N points'
a=float(raw_input("Insert a:"))
b=float(raw_input("Insert b:"))
N=int(raw_input("Insert N:"))

print 'Choose integration method‘


print '1- Rectangular method'
print '2- Trapez method'
c=raw_input('Choice= ')
if c=='1':
s=integrate_rect(a,b,N)
if c=='2':
s=integrate_trap(a,b,N)
print 'Result ', s
Per generare un extension module:
Esempio
1. Compilare .pyx:
cython myscript.pyx
2. Compilare i codici sorgenti e produrre una shared lib:
gcc -I/data/apps/bin/epd-5.0.0-rh5-x86_64/include -fPIC
-I/data/apps/bin/epd-5.0.0-rh5-x86_64/include/python2.5
-c mylib.c myscript.c

gcc -pthread -shared mylib.o myscript.o -L/data/apps/bin/


/epd-5.0.0-rh5-x86_64/lib -lpython2.5 -o myscript.so

3. A questo punto è possibili utilizzare in Python il modulo appena creato:


import myscript
Compute integration of function x^2
Between interval a, b with N points
Insert a:1
Insert b:2
Insert N:10
Choose integration method
1- Rectangular method
2- Trapez method
Choice= 1
Result 2.185
Esempio
• E’ possibile generare in automatico un extension module, utilizzando il modulo
Cython.Distutils e generando un file di setup.py
#setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

sourcefiles = ['myscript.pyx', 'mylib.c']


setup(
cmdclass = {'build_ext': build_ext},
ext_modules = [Extension("myscript", sourcefiles)])

Da lanciare con il comando:


python setup.py build
Esempio
Tipicamente Cython viene utilizzato per creare degli extension module per
Python. E’ possibile tuttavia generare dei programmi standalone,
includendo l’interprete Python nell’eseguibile prodotto.
Per generare un eseguibile:
1. Compilare .pyx con il flag --embed
cython myscript.pyx –embed
2. Compilare i sorgenti e generare l’eseguibile:
gcc -I/data/apps/bin/epd-5.0.0-rh5-x86_64/include
-I/data/apps/bin/epd-5.0.0-rh5-
x86_64/include/python2.5 -c mylib.c myscript.c

gcc myscript.o mylib.o -L/data/apps/bin/epd-


5.0.0-rh5-x86_64/lib -lpython2.5 -o my_exe
Benchmark
• Consideriamo l’equazione delle onde in un mezzo eteronegeneo, con
velocità k, nel caso bidimensionale:

 2u   u    u 
  k ( x , y )   
 k ( x , y ) 
 t x 
2
x  y  y 

• Consideriamo un dominio rettangolare   (0,1)(0,1) , con u=0


sull’intero bordo e con le seguenti condizioni iniziali:
  x  x 2  y  y 2 
u ( x, y,0)  A exp   c
   c
 
  2 x   2y  
 
u
0
t
A2 xc  yc  0.5  x   y  0.15
k ( x, y )  max( x, y )
Benchmark
• Il problema viene discretizzato attraverso uno schema alle differenze
finite:

 t 
 
2
l 1
u l i , j    ki  0.5, j (ui 1, j  ui , j )  ki 0.5, j (ui , j  ui 1, j )
 x 
2
 t 

   ki , j 0.5 (ui , j 1  ui , j )  ki , j 0.5 (ui , j  ui , j 1 )l 1

 y 

• Confrontiamo i tempi di calcolo ottenuti con diverse implementazioni


dello stesso problema
Python+Numpy

def calculate_u(dt, dx, dy, u, um, up, k):


hx = (dt/dx)**2
hy = (dt/dy)**2
for i in xrange(1, u.shape[0]-1):
for j in xrange(1,u.shape[1]-1):
k_c = k[i,j]
k_ip = 0.5*(k_c + k[i+1,j])
k_im = 0.5*(k_c + k[i-1,j])
k_jp = 0.5*(k_c + k[i,j+1])
k_jm = 0.5*(k_c + k[i,j-1])
up[i,j] = 2*u[i,j] - um[i,j] +hx*(k_ip*(u[i+1,j] - u[i,j]) - k_im*(u[i,j] - u[i-
1,j])) +hy*(k_jp*(u[i,j+1] - u[i,j]) - k_jm*(u[i,j] - u[i,j-1]))
return up
Python+Numpy

#Definizione delle varibili


m = 250; n = 250 # grid size
dx = 1.0/m
dy = 1.0/n
k = zeros((m+1, n+1))
up = zeros((m+1, n+1))
u = zeros((m+1, n+1))
um = zeros((m+1, n+1))
A=2
xc=yc=0.5
sx=sy=0.15
Python+Numpy
x=linspace(0,1,m+1)
y=linspace(0,1,n+1)
for i in xrange(0,m+1):
for j in xrange(0,m+1):
k[i,j]=max(x[i],y[j])
x,y=meshgrid(x,y)
I=A*numpy.exp(((x-xc)/2*sx)**2-((y-yc)/2*sy)**2)
u=I
dt = float(1/sqrt(1/dx**2 + 1/dy**2)/k.max())
print dt
t=0;t_stop=1.0
print 'Start'
start=time.clock()
while t <= t_stop:
t += dt
up = calculate_u(dt, dx, dy, u, um, up, k)
um[:] = u
u[:] = up
print 'Stop'
stop=time.clock()
print 'Elapsed time ', stop-start, 's'
Python+Numpy+vettorizzazione

def calculate_u(dt, dx, dy, u, um, up, k):


hx = (dt/dx)**2
hy = (dt/dy)**2 Vettorizzazione
k_c = k[1:m,1:n]
k_ip = 0.5*(k_c + k[2:m+1,1:n])
k_im = 0.5*(k_c + k[0:m-1,1:n])
k_jp = 0.5*(k_c + k[1:m,2:n+1])
k_jm = 0.5*(k_c + k[1:m,0:n-1])
up[1:m,1:n] = 2*u[1:m,1:n] - um[1:m,1:n] +hx*(k_ip*(u[2:m+1,1:n] -
u[1:m,1:n]) -k_im*(u[1:m,1:n] - u[0:m-1,1:n])) +hy*(k_jp*(u[1:m,2:n+1] -
u[1:m,1:n]) -k_jm*(u[1:m,1:n] - u[1:m,0:n-1]))
return up
F2PY
subroutine calculate_u(dt,dx,dy,u,um,up,k,n,m)
integer m,n
real*8 u(0:m,0:n),um(0:m,0:n)
real*8 up(0:m,0:n),k(0:m,0:n)
real*8 dt,dx,dy,hx,hy
real*8 k_c,k_ip,k_im,k_jp,k_jm
intent(in) u,um,k,n,m
intent(out) up
integer i,j
hx=(dt/dx)*(dt/dx)
hy=(dt/dy)*(dt/dy)
do j=1,n-1
do i=1,m-1
k_c=k(i,j)
k_ip=0.5*(k_c+k(i+1,j))
k_im=0.5*(k_c+k(i-1,j))
k_jp=0.5*(k_c+k(i,j+1))
k_jm=0.5*(k_c+k(i,j-1))
up(i,j)=2*u(i,j)-um(i,j)+hx*(k_ip*(u(i+1,j)-u(i,j))-k_im*(u(i,j)-u(i-
1,j)))+hy*(k_jp*(u(i,j+1)-u(i,j))&
-k_jm*(u(i,j)-u(i,j-1)))
end do
end do
return
end
F2PY
import numpy
from numpy import *
import time
import mymod

Fortran Function

print 'Start'
start=time.clock()
while t <= t_stop:
t += dt
up = mymod.calculate_u(dt, dx, dy, u, um, k)
um[:] = u
u[:] = up
print 'Stop'
stop=time.clock()
print 'Elapsed time ', stop-start, 's'
#Compute.pyx Cython
import numpy as np
cimport numpy as np
cimport cython
DTYPE=np.float
ctypedef np.float_t DTYPE_t
def calculate_u(float dt, float dx, float dy,np.ndarray[DTYPE_t, ndim=2, negative_indices=False] u,
np.ndarray[DTYPE_t, ndim=2, negative_indices=False] um,
np.ndarray[DTYPE_t, ndim=2, negative_indices=False] up,
np.ndarray[DTYPE_t, ndim=2, negative_indices=False] k):
cdef int m = u.shape[0]-1
cdef int n = u.shape[1]-1
cdef int i, j, start = 1
cdef float k_c, k_ip, k_im, k_jp, k_jm
cdef float hx = (dx/dt)**2
cdef float hy = (dy/dt)**2
for i in xrange(start, m):
for j in xrange(start, n):
k_c = k[i,j]
k_ip = 0.5*(k_c + k[i+1,j])
k_im = 0.5*(k_c + k[i-1,j])
k_jp = 0.5*(k_c + k[i,j+1])
k_jm = 0.5*(k_c + k[i,j-1])
up[i,j] = 2*u[i,j] - um[i,j]+hx*(k_ip*(u[i+1,j] - u[i,j]) -k_im*(u[i,j] - u[i-1,j])) +hy*(k_jp*(u[i,j+1] - u[i,j]) -k_jm*(u[i,j] - u[i,j-1]))
return up
Contronto

Griglia di calcolo 250 X 250

Processori: Intel(R) Xeon(R)


CPU X5460 @ 3.16GHz

Potrebbero piacerti anche