Python: facile, pulito, potente Magnus Lie Hetland e Alex Martelli 1 Cos'è Python? Python é un linguaggio interpretato, interattivo, orientato agli oggetti; può essere, per vari aspetti, paragonato a linguaggi come Tcl, Perl, e Java. Python combina una potenza notevole con una sintassi particolarmente limpida; la sua implementazione, estremamente portatile, é disponibile per tanti tipi di Unix, Linux, Windows, DOS, OS/2, Mac, Amiga... nella versione Jython, in particolare, Python può appoggiarsi a una macchina virtuale Java, e quindi girare dovunque Java sia disponibile (ad esempio, come applet all'interno di un browser). É un linguaggio solido e stabile: il suo sviluppo, iniziato dall'autore Guido van Rossum nel 1990 presso il CWI di Amsterdam, continuó poi al CNRI di Reston, Virginia, successivamente presso i PythonLabs della BeOpen Inc., e oggi presso l'azienda Digital Creations. Tutte le varie implementazioni e le librerie di Python sono Open Source. Vari siti Web come [http://www.python.org], [http://www.pythonware.com/daily/], [http://www.vex.net/~x/parnassus/] (e, in italiano, il sito [http://web.tiscalinet.it/python]) contengono molto materiale utile sul linguaggio, e una dovizia di puntatori ad altri materiali. 2 Interpretato o compilato? Python, come Java o Perl, é in realtà entrambe le cose -- sia compilato, sia interpretato. Il compilatore Python trasforma il testo (codice sorgente) dei nostri programmi in un codice intermedio, detto byte-code, che può essere visto come "codice-macchina" per una macchina "virtuale" studiata apposta per eseguire programmi Python; l'interprete Python vero e proprio é una implementazione in software di questa macchina virtuale. Come in Perl, e a differenza che in Java, si può richiedere direttamente l'esecuzione dei file-sorgente (normalmente con estensione .py), senza necessità di un esplicito passaggio di compilazione; come in Java, e a differenza che in Perl, il byte-code non viene solo preparato "al volo" in memoria, ma viene anche salvato su disco (con estensione .pyc), cosí che in esecuzioni successive non occorrerà compilarlo nuovamente (la compilazione é però automaticamente ri-eseguita se il file sorgente é piú aggiornato di quello contenente il byte-code). Esistono compilatori Python che producono codice per altre macchine virtuali: Jython produce bytecode per una JVM (file .class), Python.NET produce il codice-intermedio MSIL tipico della nuova piattaforma .NET (file .msil). La "versione-base" di Python, essendo scritta in C (sia il compilatore, sia la macchina virtuale), è anche denominata C-Python ove occorra distinguerla da Jython e Python.NET; è sul classico C-Python che ci concentreremo in questo articolo (non che le altre versioni ne differiscano molto!). 3 Per cosa si usa Python? Come altri linguaggi di scripting, come Tcl e Perl, Python é particolarmente adatto per compiti come le operazioni di Web (server-side, sotto forma di script CGI, ovvero con architetture piú moderne, come FastCGI, mod_python, etc; client-side, "entro il browser", opzione per cui Python offre varie possibilità -- JPython permette di usare Python per scrivere applet al posto di Java, Active Scripting permette di usare Python al posto, ad esempio, di Javascript -- esiste anche un browser, detto Grail, scritto interamente in Python). Lo scripting (Python in particolare) é poi ideale per vari compiti di gestione di sistema (al posto dei deboli, scomodi shell-script), come colla fra componenti scritti in altri linguaggi (ad esempio, Python é ampiamente usato a questo scopo nell'ambito del calcolo scientifico), e, facilmente incorporato in applicazioni (magari ricche e complesse), per la personalizzazione e automatizzazione delle applicazioni stesse. Ma, per Python, questo é solo l'inizio. La sua unica combinazione di potenza, semplicità, e pulizia, lo rendono ideale per compiti di prototipazione (sviluppo esplorativo di componenti che, se occorre, possono poi eventualmente essere reimplementati ad esempio in C) e anche per la scrittura di interi, complessi sistemi (Zope, [http://www.zope.org], é ad esempio interamente in Python, e lo stesso può dirsi per molta dell'infrastruttura di popolari servizi disponibili in rete, come eGroups, Google, Infoseek, Yahoo, ...). Sempre a titolo di esempio, segnaliamo anche un semplice DBMS relazionale SQL (transazionale, con la possibilità di uso client/server in TCP/IP), Gadfly, interamente scritto in Python -- vedi [http://www.chordate.com/kwParsing/gadfly.html] (le sue prestazioni su database grossi possono essere sostanzialmente migliorate usando, per i bassissimi livelli, il modulo scritto in C 'kjBuckets'; e anche questo è tipico: una volta che l'intera architettura di un sistema, sviluppata in Python, permette di individuare i colli di bottiglia computazionali, un'ultima ottimizzazione può compiersi codificando in C o C++ i bassissimi livelli di questi colli di bottiglia). Anche Gadfly è liberamente e gratuitamente scaricabile ed utilizzabile, secondo il modello Open Source. 4 Da dove comincio? Scarica e installa l'ultima versione di Python dal sito [http://www.python.org/] ([footnote] Gli esempi che seguono si riferiscono a Python versione 2.0 o superiore.) (puoi trovare il tarball dei sorgenti o il pacchetto RPM direttamente da installare, oltre alle versioni di altri sistemi operativi e la documentazione da leggere on-line o da stampare). Accertati che la directory d'installazione sia nel PATH (automatico se installi da RPM). Hai ora a disposizione un ambiente integrato di sviluppo (si chiama idle, e comprende editor specializzato, interprete interattivo, debugger, class browser, documentazione...) e un piú semplice programma chiamato python, meno ricco e potente ma piú facile da imparare ad usare -- ci concentriamo su quest'ultimo, ma conviene sicuramente anche esplorare idle, che alla lunga può offrire notevoli comodità. Eseguendo python senza argomenti da qualsiasi shell, otterrai una risposta del tipo: Python 2.0b2 (#6, Sep 26 2000, 14:59:21) [gcc 2.9.37 (Intel)] on linux Type "copyright", "credits" or "license" for more information. >>> e puoi immettere interattivamente codice Python al prompt '>>>' -- verrà eseguito immediatamente, il che é comodo per provare. Ad esempio, immettendo la riga di codice: print "Hello, world!" il messaggio verrà visualizzato. Alla fine, premerai Ctrl-D per terminare la sessione interattiva di Python. Per programmare "davvero", scriverai il codice Python, con qualsiasi editor di testi (ad esempio, vim), in un file di testo (chiamato, ad esempio, hello.py), e lo eseguirai (da una shell) col comando: python hello.py se hello.py contiene la stessa, unica riga print "Hello, world!", anche questo mostrerà lo stesso messaggio. Esistono inoltre altri modi di rendere direttamente eseguibile un file di testo contenente un programma (script) Python: su Linux, basta renderlo eseguibile con il comando chmod +x e assicurarsi che inizi con una prima riga del tipo: #!/usr/bin/env python Il programma hello.py potrà ora venire eseguito direttamente digitandone il nome (ed eventualmente il directory, come per qualsiasi altro programma) da qualsiasi shell. 5 Com'é fatto Python? Python é praticamente pseudo-codice eseguibile. Una variabile non ha tipo e non viene dichiarata: appare quando le si assegna qualcosa, sparisce quando non la si usa piú. L'assegnazione si ottiene con l'operatore = ; l'eguaglianza si controlla con l'operatore ==. Si possono assegnare molteplici variabili simultaneamente: * x,y,z = 1,2,3 * prima, seconda = seconda, prima * a = b = 123 I blocchi sono indicati dall'indentazione, e solo dall'indentazione (niente BEGIN/END, niente parentesi graffe). Una struttura di controllo fondamentale é if (questi due esempi sono equivalenti) : if x < 5 or (x > 10 and x < 20): print "Il valore é OK." # qui siamo dentro il blocco if # qui siamo fuori dal blocco if if x < 5 or 10 < x < 20: print " Il valore é OK." Ci sono poi i cicli: for i in [1,2,3,4,5]: print "Iterazione numero", i x = 10 while x >= 0: print "x non é ancora negativo" x = x-1 La variabile-indice del ciclo for itera sugli elementi di una lista (una costante lista può essere scritta con parentesi quadre e virgole, come nell'esempio). Per fare un "normale" ciclo di for (cioè uno in cui la variabile indice itera su di una sequenza di numeri), si può usare la funzione intrinseca range(): # Stampa i numeri da 0 a 99 inclusi for value in range(100): print value (La riga che inizia con # é un commento, e viene quindi ignorata da Python). Attenzione: Python (come Java, C, C++,...) é case-sensitive -- x e X sono due variabili differenti... non é un problema, ma attenzione che questo non provochi errori! 6 I/O in Python Quanto visto finora basta (in teoria) per implementare qualsiasi algoritmo in Python ed emettere i risultati su standard output. Aggiungiamo alcuni fondamenti di input/output. Per avere input dall'utente tramite un prompt di testo, si usa la funzione intrinseca input: x = input("Immetti un numero: ") print "Il quadrato di", x, "é", x*x La funzione input mostra il prompt passatole (che può essere vuoto) e aspetta che l'utente immetta una qualsiasi espressione Python. In questo caso ci serve un numero -- se viene immesso qualcosa d'altro, tipo una stringa, il programma va in errore (queste eccezioni possono essere gestite, ma qui non ce ne occuperemo). Se si desidera che l'input dell'utente sia mantenuto alla lettera come stringa (stringa, allora, del tutto arbitraria), si userà invece la funzione intrinseca raw_input (per convertire una stringa s ad intero, si potrà poi usare int(s)).([footnote] Per immettere una stringa in risposta ad input, l'utente dovrá usare le virgolette (le stringhe Python possono essere racchiuse fra virgolette semplici o doppie, non fa alcuna differenza).) Spesso si vogliono leggere e scrivere file di testo; anche questo é facile -- ad esempio, per copiare "fin.txt" su "fou.txt" si può scrivere (leggendo subito tutte le righe, poi eseguendo un ciclo su tutto l'insieme delle righe lette): fou=open("fou.txt","w") for line in open("fin.txt").readlines(): fou.write(line) In alternativa, e in modo del tutto equivalente, si puó leggere una sola riga alla volta: fou=open("fou.txt","w") fin=open("fin.txt","r") while 1: line = fin.readline() if not line: break fou.write(line) (break interrompe il ciclo corrente). Il secondo argomento della funzione intrinseca open dice se stiamo aprendo il file in lettura o in scrittura; é facoltativo, e il default é la lettura. readlines restituisce tutte le righe, in sequenza, pronte ad esempio per un'interazione con for; readline (al singolare, cioè senza la s finale) restituisce solo la prossima riga ogni volta che viene chiamata (ovvero, un valore falso, se non vi sono piú righe da restituire). Un'altra possibilità molto comoda per la lettura di file di testo é offerta dal modulo fileinput, che illustreremo fra breve. A volte, si vogliono formattare dei dati ottenendo una stringa (ad esempio per emetterla); questo si può fare con l'operatore %, che prende sulla sinistra una stringa di formato (molto simile alla stringa di formato di una printf del C) e sulla destra una sequenza di valori, e restituisce una stringa. Ad esempio, per numerare le righe emesse, il ciclo potrebbe essere modificato nel modo seguente : fou=open("fou.txt","w") fin=open("fin.txt","r") i=0 while 1: line = fin.readline() if not line: break i=i+1 fou.write("%d: %s" % i, line) 7 Liste Sinora, abbiamo illustrato le strutture di controllo e l'input/output -- ora, ci servono delle belle strutture dati. Le piú importanti sono liste e dizionari. Le liste si scrivono con parentesi quadre e virgole, e (naturalmente) possono anche essere annidate: name = ["Cleese", "John"] x = [ [1,2,3], [y,z], [ [ [ ] ] ] ] Data una lista, se ne possono accedere gli elementi separatamente o a gruppi, con i costrutti di indexing e slicing. L'indexing si ottiene (come in molti altri linguaggi) giustapponendo l'indice fra parentesi quadre alla lista (il primo elemento ha indice 0): >>> print name[1],name[0] John Cleese >>> name[0] = "Smith" Lo slicing (alla lettera, affettamento) é quasi come l'indexing, eccetto che si scrivono sia l'indice iniziale sia quello finale del risultato, con un carattere di due-punti (":") per separarli: >>> x=['spam','spam','spam','spam','spam','eggs','and','spam'] >>> print x[5:7] ['eggs','and'] Notiamo che la fine é non-compresa. Se manca uno degli indici nello slicing, si prende "tutto in quella direzione". Ad esempio, list[:3] significa "ogni elemento dall'inizio di list sino all'elemento 3, non compreso", cioè i primi 3 elementi, esattamente come list[0:3]. Invece, list[3:] significa "ogni elemento di list a partire dall'elemento 3 (compreso) sino all'ultimo (compreso)". Sia nell'indexing, sia nello slicing, si possono usare anche indici negativi, che indicano di "contare dalla fine" : list[-3], ad esempio, indica il terzo elemento dalla fine della lista. Si puó anche assegnare una lista di qualsiasi lunghezza ad una slice di una lista: y = ["fee","fie","foo","fum"] y[1:-1] = ["bar","baz","bat"] mette in y la lista ['fee', 'bar', 'baz', 'bat', 'fum']: infatti, la "slice" escludeva solo il primo e l'ultimo elemento (quindi, si trattava dei due elementi "fie" e "foo"), ed é stata sostituita, dall'istruzione di assegnamento, con i tre elementi elencati. La funzione intrinseca len dà la lunghezza di una lista; ad esempio, per qualsiasi lista a, a[len(a)-1] é sempre esattamente equivalente ad a[-1] (l'ultimo elemento della lista). Tutto questo funziona anche con le stringhe: s="internazionalizzazione" print s[5:7] # emette la stringa "na" eccetto che, per le stringhe, non si possono fare assegnamenti ai risultati di slicing e indexing, perché le stringhe, a differenza delle liste, sono immutabili. Le liste hanno anche altri metodi di mutazione (non applicabili alle stringhe, per la stessa ragione), come mylist.append(value), che aggiunge un valore alla fine della lista (allungandola quindi di un elemento), e mylist.sort(), che permuta gli elementi della lista per assicurare che essa sia ordinata. Le liste (e le stringhe) possono essere concatenate usando l'operatore +, e replicate un certo numero di volte usando l'operatore *: l = ['a', 'b'] + ['c', 'd'] * 3 # l diventa ['a','b','c','d','c','d','c','d'] 8 Dizionari I dizionari sono simili alle liste, ma i loro contenuti non sono in sequenza. Per indicizzarli, si usa invece il fatto che ogni elemento é associato a una chiave, o "nome", che viene usato per reperire l'elemento, proprio come in un vero dizionario. Un paio di esempi: telefoni = {"Alice":23452532,"Boris":252336,"Clarice":2352525,"Doris":23624643} persona = {'nome':"Robin",'cognome':"Hood",'professione':"Fuorilegge"} Per avere la professione della persona, useremo l'espressione persona["professione"]. Per cambiare il cognome, potremmo scrivere: persona['cognome'] = "di Locksley" e, identicamente, per aggiungere al dizionario un elemento non già presente: persona['specialità'] = "Arciere" Piuttosto semplice. Notiamo che, come le liste, i dizionari possono contenere altri dizionari (o liste -- e naturalmente, anche le liste possono contenere dizionari; si possono facilmente comporre strutture dati piuttosto avanzate). A partire da un dizionario dict, si può avere la sequenza delle sue chiavi (ad esempio, per iterarci con un for) chiamando dict.keys(): for key in dict.keys(): print key,dict[key] Questo é un idioma molto comune, quindi c'é un'alternativa: for key,value in dict.items(): print key,value Questo svolge lo stesso compito, poichè dict.items() restituisce una lista di coppie chiave/valore. Inoltre, dict.values() restituisce la lista dei valori (comodo se non interessano le relative chiavi); dict.has_key(key) restituisce un valore vero se la chiave é fra quelle attualmente presenti nel dizionario, altrimenti un valore falso. L'operatore di formato, %, può accettare un dizionario come argomento di destra, e in questo caso la stringa di formato comprenderà anche i nomi (chiavi nel dizionario) da usare: >>> "%(nome)s %(cognome)s é un %(professione)s" % persona 'Robin Hood é un Fuorilegge' La funzione intrinseca locals() restituisce un dizionario le cui coppie chiave/valore sono i nomi e i valori delle variabili locali, e similmente globals(), mentre vars() li fonde entrambi (con priorità alle variabili locali in caso di sovrapposizione, che é la normale [e piuttosto intuitiva...] regola del Python); sono molto comodi, ad esempio, per l'uso con l'operatore di formato %, per ottenere l'effetto della cosiddetta interpolazione di variabili di altri linguaggi (con un controllo assai piú preciso di quello permesso da questi altri linguaggi): "x=%(x)s, y=%(y)s, e z= %(z)s" % vars() é un modo comodo di emettere i valori attuali delle variabili x, y e z, ciascuno con la propria identificazione. 9 Stringhe Abbiamo già usato sin dall'inizio le stringhe di Python, che in effetti si prestano ad un uso elementare assai intuitivo, ma é il caso di notare che, come un po' tutto in Python, anche le stringhe sono oggetti, dotate dei loro metodi; ad esempio, come già accennato, possono sempre essere usate come sequenze (immutabili) di caratteri, con indexing e slicing, uso in for, ecc. Hanno inoltre metodi particolari come lower (che restituisce la versione in minuscole della stringa) e split (che restituisce la lista delle varie parole presenti nella stringa, nell'ordine). Per esempio, creiamo un indice analitico per un grosso file di testo big.txt: una lista alfabetica delle parole che contiene, una per riga, ciascuna seguita dai numeri delle righe in cui si trova. fin=open("big.txt") numero_riga=0 indice={ } # un dizionario vuoto while 1: # ciclo input primario (righe in ingresso) riga=fin.readline() if not riga: break numero_riga=numero_riga+1 parole=riga.lower().split() # lista delle parole, in minuscolo for parola in parole: # ciclo input secondario (parole della riga) if indice.has_key(parola): indice[parola].append(numero_riga) # aggiunge 1 elemento else: indice[parola]=[numero_riga] # lista di 1 solo elemento parole=indice.keys() parole.sort() for parola in parole: # ciclo output primario (sulle parole riscontrate) print parola,": ", for numero_riga in indice[parola]: # ciclo output secondario (numeri riga) print numero_riga, print L'istruzione print normalmente termina la riga di testo che emette, ma qui la usiamo con una virgola alla fine, il che elimina questa caratteristica e richiede invece un separatore "spazio"; in questo modo, la parola e i numeri delle righe che la contengono vengono emessi sulla stessa riga, separati da spazi. 10 Moduli Python offre una discreta funzionalità attraverso caratteristiche intrinseche (tipi di dati, operatori, funzioni), ma la maggior parte della sua potenza è messa a disposizione dai moduli -- pacchetti separati di funzionalità, che vanno esplicitamente importati alla bisogna. I moduli a loro volta possono essere intrinseci (Python ne ha parecchi), possono essere file scritti da noi (qualsiasi file .py può essere importato come modulo, e si possono anche scrivere altri moduli in C o C++), o li si può ottenere da terze parti ed installarseli (c'è un enorme numero di meravigliosi moduli Python disponibili per il download gratuito dalla rete, naturalmente). Per esempio, un modulo intrinseco che potrebbe fare comodo per lo stesso compito del paragrafo precedente è fileinput, che permette di leggere uno o piú file di testo in sequenza, tenendo conto del numero di linea e con varie piccole comodità. Usandolo, il ciclo principale diventa assai piú semplice (visto che sfruttiamo la possibilità di iterare con for, e la funzione lineno() che tiene già traccia del numero progressivo di riga): import fileinput filein=fileinput.input("big.txt") indice={ } # un dizionario vuoto for riga in filein: # ciclo input primario (righe in ingresso) parole=riga.lower().split() # lista delle parole, in minuscolo for parola in parole: # ciclo input secondario (parole della riga) if indice.has_key(parola): indice[parola].append(filein.lineno()) # aggiunge un elemento else: indice[parola]=[filein.lineno()] # lista di 1 solo elemento seguito, naturalmente, dallo stesso ciclo di uscita già usato all'esempio precedente. Le ultime quattro righe si potrebbero, alternativamente, riassumere in una sola, usando un metodo piú avanzato dell'oggetto-dizionario cui fa riferimento la variabile indice : indice.setdefault(parola,[]).append(filein.lineno()) Python lascia queste scelte di stile (concisione e massima potenza, ovvero espressione dettagliata di massima semplicità elementare) all'apprezzamento del programmatore! 11 Funzioni Per costruire dei moduli (e non solo per questo), abbiamo chiaramente bisogno di definire noi stessi delle funzioni (o procedure che dir si voglia), cioè dei brani di codice, con un nome, che possono essere chiamati con dei parametri. A questo scopo Python utilizza la parola-chiave def: def quadrato(x): return x*x print quadrato(2) # Emette 4 La parola-chiave return termina l'esecuzione della funzione, e ritorna come suo risultato il valore dell'espressione passatale (in mancanza, il valore speciale None, che è l'oggetto nullo del Python). Se scriviamo queste due righe che definiscono la funzione quadrato in un file di testo chiamato matematica.py (e lo poniamo nel directory corrente, o in un directory elencato nella variabile d'ambiente PYTHONPATH), da qualsiasi altro programma Python potremo poi scrivere: import matematica print matematica.quadrato(2) # Emette 4 oppure, se vogliamo incorporare solo le funzioni del modulo (non il modulo): from matematica import * print quadrato(2) # Emette 4 Tutti i parametri in Python sono passati per riferimento (come, ad esempio, in Java, e per ragioni simili, cioè che tutto funziona per riferimento in Python, come in Java; ci torneremo piú avanti) e possono avere valori di default (come in C++, ma anche con un nome, come in Visual Basic): def cat(s1, s2, rep1=1, rep2=1): return s1*rep1+s2*rep2 cat('fee','fie') # ok, vale 'feefie' -- rep1 e rep2 entrambi da default cat('fee') # errore -- s2 non ha default, quindi è obbligatorio cat('foo','bar',3) # restituisce 'foofoofoobar' cat('foo','bar',rep2=3) # restituisce 'foobarbarbar' Per chiamare una funzione, anche senza argomenti, bisogna sempre usare le parentesi, perchè se una funzione è nominata senza parentesi, si ha come risultato la funzione stessa; le funzioni sono valori di prima classe in Python...! Possiamo avere liste o dizionari contenenti funzioni, passare funzioni come argomenti e restituirle come risultati di altre funzioni, e anche semplicemente definire e usare dei sinonimi per esse, come ad esempio: join=cat # join diventa un "sinonimo" della funzione cat join('a','b',3,2) # restituisce 'aaabb' 12 Oggetti e riferimenti Abbiamo già usato gli oggetti, piú che altro perchè ogni elemento di dati in Python è un oggetto... oltre ad avere (o essere) dati, ha dei metodi -- funzioni che si possono chiamare con la sintassi, tipica appunto della programmazione ad oggetti, oggetto.funzione(argomenti). Per esempio, abbiamo già visto che, data una lista in una variabile denominata L, chiamare L.sort() modifica la lista cosí che divenga ordinata; dato un dizionario in una variabile denominata D, chiamare D.values() restituisce una lista dei valori attualmente contenuti nel dizionario; e cosí via. Inoltre, parlando di I/O, abbiamo nominato la funzione intrinseca open, che ritorna un oggetto file, su cui possiamo chiamare metodi come readline e write. Naturalmente, Python ci permette di creare dei nostri oggetti e determinare i loro metodi; linguaggio profondamente ad oggetti, ci mancherebbe altro che non rendesse perfettamente agevole definirne dei nuovi! Ma in questo articolo, per mancanza di spazio, non parleremo della facilità con cui Python permette di svolgere questi compiti; ci sono aspetti che vanno ben capiti prima di indirizzarsi a questi, e, in particolare, dobbiamo chiarire il concetto di riferimento. Ogni variabile Python, in qualsiasi momento, contiene un riferimento a un qualche oggetto; cosí pure ogni elemento di una lista, e ogni chiave e ogni valore di un dizionario. Spesso, questo aspetto non ha importanza, perchè il riferimento è a un oggetto immutabile -- qualsiasi numero è immutabile, e cosí pure qualsiasi stringa (le chiavi di un dizionario, in particolare, devono essere riferimenti a oggetti immutabili -- a questo scopo, c'è anche una specie di lista immutabile, detta tuple, che, sintatticamente, differisce dalla lista quasi solo nel fatto che le costanti che sono tuple si scrivono con parentesi tonde, invece delle parentesi quadre che sono usate per le costanti che sono liste). Ma, se abbiamo riferimenti a un oggetto mutabile, allora, se l'oggetto cambia, tutti i riferimenti implicitamente indicano il nuovo oggetto dopo il cambiamento: >>> x=['a','b','c'] >>> y=x >>> x[1]='r' >>> print y ['a','r','c'] y si riferisce allo stesso oggetto cui si riferisce x, e questo oggetto è cambiato. Questo è spesso ciò che si desidera; quando non lo è, invece di aggiungere semplicemente un riferimento, come fa l'istruzione di assegnamento y=x, dobbiamo fare una copia, per esempio cambiando l'istruzione di assegnamento a: y=x[:] Questo idioma di slice dall'inizio alla fine genera un nuovo oggetto, una copia di quello cui fa riferimento x, ed è alla copia che fa riferimento y. Di conseguenza, futuri cambiamenti della lista cui fa riferimento x non avranno nessun effetto sull'oggetto cui fa riferimento y, e viceversa. Python fornisce anche un comodo modulo copy che gestisce per noi queste considerazioni: import copy y=copy.copy(x) esegue un'operazione simile a y=x[:], ma lo fa polimorficamente (cioè, a qualsiasi cosa x faccia riferimento, che sia una lista, un dizionario, una stringa, una tupla, ...) cosí che noi non dobbiamo preoccuparci dei dettagli; e y=copy.deepcopy(x) si spinge anche piú a fondo, copiando ricorsivamente anche ciò a cui gli elementi di x fanno a loro volta riferimento. 13 Altri tipi intrinseci Oltre a numeri (interi e reali), stringhe, liste, dizionari, funzioni, e vari altri tipi la cui esistenza è stata già implicata (come gli oggetti-file restituiti dalla funzione open), Python offre vari altri tipi intrinseci, comodi e potenti nelle applicazioni in cui servono. Ad esempio, i numeri complessi, indicati posponendo una j alla loro parte immaginaria: >>> x=2+2j >>> x*(1-1j) (4+0j) >>> x.real 2.0 >>> x.imag 2.0 col relativo complemento di funzioni matematiche, ottenute con import cmath cosí come quelle sui numeri reali si ottengono con import math: >>> import cmath >>> cmath.sqrt(-1) 1j >>> cmath.sin(1j) 1.17520119364j I numeri complessi, naturalmente, sono utili solo per certi particolari compiti matematici. Per altri compiti, come quelli di aritmetica combinatoria, tornano invece utili gli interi lunghi (cioè con precisione illimitata), indicati posponendo una L alle loro cifre...: def fact(x): if x<=1: return 1L else: return x*fact(x-1) Questo ci permette di calcolare facilmente in quanti diversi modi può essere mescolato un normale mazzo di carte: >>> fact(52) 80658175170943878571660636856403766975289505440883277824000000000000L o quale sia la probabilità, ad esempio, che una mano di 13 carte (su 52) abbia esattamente cinque carte di picche (sulle 13 picche del mazzo): def comb(m,n): return fact(m)/(fact(n)*fact(m-n)) >>> comb(52,13) 635013559600L questo è il numero di diverse mani di 13 carte su 52, >>> comb(13,5)*comb(39,8) 79181063676L questo è il numero di mani con esattamente 5 picche e 8 non-picche, >>> comb(13,5)*comb(39,8)/float(comb(52,13)) 0.12469192583206691 e quindi questa è la probabilità che cercavamo (circa il 12.47%). Utili per scopi di "internazionalizzazione" dei programmi sono le stringhe Unicode, con gli stessi metodi delle stringhe "normali" e un'amplissima gamma di codifica/decodifica: >>> u"Cioé".encode("iso-8859-1") 'Cio\351' >>> u"Cioè".encode("utf-8") 'Cio\303\251' Gli altri tipi intrinseci sono forniti da moduli pure intrinseci, tramite un esplicito import; gli esempi sono numerosi, come le regular expressions nel modulo re, gli array nel modulo array, gli interpreti interattivi nel modulo cmd, ecc.