Traduzione della FAQ di http://www.python.org aggiornata al 6 settembre 2005, Revisione 8232.
Python è un linguaggio di programmazione interpretato, interattivo ed orientato agli oggetti. Incorpora al suo interno moduli, eccezioni, tipizzazione dinamica, tipi di dati dinamici di alto livello e classi. Python riesce a combinare una notevole potenza ad una sintassi estremamente chiara. Possiede librerie ed interfacce per molte chiamate di sistema, come per vari ambienti grafici, ed è estendibile in C o C++. Può anche essere utilizzato come linguaggio di estensione per quelle applicazioni che necessitano di un'interfaccia programmabile. Infine, Python è portabile: funziona su molte distribuzioni di Unix, in Mac, e sui PC sotto MS-DOS, Windows, Windows NT e OS/2.
Per saperne di più, un buon punto di partenza è la Beginner's Guide to Python [traduzione Guida di Python per i principianti NdT].
Segue qui sotto un brevissimo riassunto, scritto da Guido van Rossum, che chiarisce come tutto sia iniziato.
Ho maturato una lunga esperienza nell'implementazione di un linguaggio interpretato quando collaboravo con il gruppo ABC presso CWI; ciò mi ha permesso di imparare molto riguardo la progettazione di un linguaggio. Fu l'origine di molte caratteristiche del Python, compreso l'uso dell'indentazione per il raggruppamento delle istruzioni, oltre all'inclusione di tipi di dati di alto livello (sebbene alcuni dettagli di Python siano totalmente diversi).
Nel linguaggio ABC c'erano molti aspetti di cui mi lamentavo, altri che adoravo. Era impossibile estendere il linguaggio ABC (o la sua implementazione) per rimediare ai miei motivi d'insoddisfazione -- infatti proprio la mancanza di estendibilità era una delle maggiori problematiche. Avevo una certa esperienza nell'usare Modula 2+ e oltre a discutere con i progettisti di Modula-3 avevo letto anche il suo rapporto. Modula-3 costituisce la base di partenza per la sintassi e semantica adottata in Python nella gestione delle eccezioni e di altri aspetti.
Facevo parte del gruppo di lavoro del CWI che seguiva il sistema operativo distribuito Amoeba. Siccome Amoeba possedeva un'interfaccia per le chiamate di sistema difficilmente accessibile dalla shell Bourne, avevamo bisogno di trovare un modo migliore per l'amministrazione del sistema, rispetto a scrivere programmi in C oppure script per la shell Bourne. La mia esperienza maturata con la gestione degli errori in Amoeba mi ha permesso di comprendere quanto fosse importante, come caratteristica, la gestione delle eccezioni in un linguaggio di programmazione.
Mi era venuto in mente che le nostre necessità potevano venir soddisfatte da un linguaggio di scripting con una sintassi simile ad ABC, ma che avesse l'accesso alle chiamate di sistema di Amoeba. Sapevo che sarebbe stato assurdo scrivere un linguaggio proprietario per Amoeba, così decisi di aver bisogno di un linguaggio che fosse estendibile in modo generico.
Avendo a disposizione molto tempo, durante le vacanze di Natale del 1989 ci provai. Nel corso dell'anno successivo, mentre ci lavoravo durante la maggior parte del tempo libero, Python è stato impiegato nel progetto Amoeba con molto successo ed i consigli ricevuti dai miei colleghi mi hanno consentito di realizzare subito molti miglioramenti.
Nel febbraio 1991, dopo poco più di un anno di sviluppo, ho deciso di pubblicarlo su USENET. Il resto della storia e' nel file Misc/HISTORY.
Python è a tutti gli effetti un linguaggio di alto livello che può essere impiegato per affrontare diverse tipologie di problemi.
Il linguaggio viene corredato di una corposa libreria standard che affronta aspetti quali l'elaborazione delle stringhe (espressioni regolari, Unicode, calcolo delle differenze fra più file), i procolli Internet (HTTP, FTP, SMTP, XML-RPC, POP, IMAP, programmazione CGI), l'ingegneria del software (testing di unità, logging, profiling, parsing di codice Python) e le interfacce al sistema operativo (chiamate di sistema, filesystem, socket TCP/IP). Si veda l'indice della Library Reference [traduzione Libreria di Riferimento NdT] per avere un'idea di ciò che si ha a disposizione. Una notevole varietà di estensioni sono disponibili da terze parti. Si consulti L'indice del Package Python per trovare i pacchetti che più interessano.
Le versioni di Python sono numerate A.B.C oppure A.B. A rappresenta il numero della versione principale -- viene aumentato solamente in caso di variazioni veramente importanti del linguaggio. B è il numero della versione secondaria, incrementato per quei cambiamenti che non alterino in modo sostanziale il linguaggio. C è il numero meno importante che viene incrementato ad ogni versione che risolva un bug. Si veda PEP 6 per maggiori informazioni in merito alle versioni di correzione dei bug.
Non tutti i rilasci vengono resi disponibili per risolvere di un bug: esistono versioni di sviluppo, nominate alfa, beta o candidate. Le "alfa" sono le versioni preliminari con interfacce ancora incomplete: non ci si aspetta di vedere dei cambiamenti sostanziali a livello di interfaccia fra due versioni alfa. Le "beta" sono più stabili, conservano le interfacce esistenti aggiungendo, se necessari, nuovi moduli. Le "candidate" sono identiche, eccetto per quanto sia necessario per risolvere un bug.
Le versioni alfa, beta e candidate hanno un ulteriore suffisso. Rispettivamente, i suffissi sono "aN", "bN" e "cN" dove N è un numero intero piccolo. In altre parole, le versioni chiamate 2.0aN, precedono le versioni 2.0bN che a sua volta precedono le 2.0cN; tutte vengono rilasciate prima della 2.0.
Esistono anche versioni con il suffisso "+", per esempio "2.2+". Queste non sono versioni rilasciate ufficialmente, ma create direttamente dal troncone CVS. Praticamente, dopo che viene sviluppata una versione finale secondaria, il troncone CVS viene incrementato sino alla seguente versione, che diventa la versione "a0": ad esempio "2.4a0".
Si consulti anche la documentazione di sys.version, sys.hexversion, e sys.version_info.
Minime. Si possono utilizzare i sorgenti per quello che si vuole, purchè rimangano inalterati i copyright presenti e si riportino in qualsiasi documentazione si voglia produrre inerente Python. Se vengono rispettate le regole di copyright, Python lo si può usare anche per scopi commerciali, come vendere sue copie in formato di sorgenti o binari (identici o variati) oppure per vendere dei prodotti che lo incorporino all'interno in qualche forma. Naturalmente, vorremmo essere informati di tutti gli impieghi commerciali di Python.
Si veda la pagina della licenza PSF per trovare ulteriori spiegazioni ed un link al testo completo della licenza.
La distribuzione completa dei sorgenti Python è sempre disponibile su http://www.python.org/download/. L'ultimo aggiornamento può essere ottenuto mediante CVS anonimo da SourceForge, presso http://www.sourceforge.net/projects/python.
E' contenuta in un archivio compresso tar comprendente i sorgenti C, la documentazione in formato LaTeX, i moduli della libreria Python, programmi d'esempio, e diversi utili frammenti di software liberamente distribuibile. Può essere compilato ed eseguito senza modifiche sulla maggior parte delle piattaforme UNIX
Le versioni precedenti di Python sono disponibili presso python.org.
Tutta la documentazione è disponibile on-line, a partire da http://www.python.org/doc/.
E' anche disponibile la documentazione standard per la versione corrente definitiva di Python presso http://docs.python.org/ [traduzione http://www.python.it/doc/Python-Docs/html/ NdT].
I sorgenti LaTeX della documentazione fanno parte della distribuzione dei sorgenti. Se non si usa il LaTeX, la più aggiornata documentazione è disponibile in vari formati, come PostScript e HTML, mediante FTP anonimo. Si visiti l'URL sopra riportato per conoscere i link alle versioni correnti.
Sono disponibili numerosi libri e tutorial. Si consulti the Beginner's Guide [traduzione Guida di Python per i principianti NdT] per informazioni adatte ai programmatori Python principianti, compresa una lista di tutorial.
Si consulti la lista dei mirror di python.org: http://www.python.org/Mirrors.html.
Esiste un newsgroup, comp.lang.python, ed una lista di discussione, python-list [lista di discussione italiana presso it.comp.lang.python NdT]. Il newsgroup e la lista di discussione sono collegati tra loro mediante gateway -- se si riescono a leggere le news non è necessario iscriversi alla lista. comp.lang.python è molto trafficata, riceve centinaia di interventi ogni giorno, ed i lettori di Usenet sono sempre più efficienti nel gestire questo volume.
Annunci di nuove versioni ed eventi sono visionabili in comp.lang.python.announce, una lista moderata con un traffico basso che riceve circa cinque interventi al giorno. E' disponibile presso the python-announce mailing list [qualcosa di simile in italiano è presente presso Novità su Python NdT].
Maggiori informazioni relative ad altre liste di discussioni o newsgroup sono presenti presso http://www.python.org/community/lists.html.
Tutte le versioni comprese le alfa, beta e candidate vengono annunciate nei newsgroup comp.lang.python e comp.lang.python.announce. Tutte le novità compaiono anche nella pagina principale di Python presso http://www.python.org/; assieme ad un notiziario RSS.
Si può anche accedere mediante CVS alla versione in fase di sviluppo di Python. Si veda http://sourceforge.net/cvs/?group_id=5470 per maggiori dettagli. Se non si è familiari con CVS, una sua introduzione è fornita da documenti come http://linux.oreillynet.com/pub/a/linux/2002/01/03/cvs_intro.html .
Per segnalare un bug oppure far presente dell'esistenza di una patch, si utilizzi gentilmente il servizio predisposto appositamente dal progetto Python presso SourceForge.
Per i Bug: http://sourceforge.net/tracker/?group_id=5470&atid=105470
Per le Patch: http://sourceforge.net/tracker/?group_id=5470&atid=305470
Si deve possedere un account SourceForce per poter segnalare bug; ciò ci rende possibile contattarvi se abbiamo bisogno di chiarimenti in merito. Inoltre permette a SourceForce di inviarvi gli aggiornamenti non appena abbiamo risolto il bug.
Per maggiori informazioni su come Python è sviluppato si consulti la the Python Developer's Guide.
La migliore soluzione è fare riferimento al libro che preferite inerente Python.
Il primo articolo relativo a Python è molto vecchio e oramai antiquato.
Guido van Rossum and Jelke de Boer, "Interactively Testing Remote Servers Using the Python Programming Language", CWI Quarterly, Volume 4, Issue 4 (December 1991), Amsterdam, pp 283-303.
Certo, ce ne sono molti ed altri stanno per essere pubblicati. Per ottenerne una lista si veda il python.org Wiki presso http://www.python.org/moin/PythonBooks.
Si possono anche ricercare librerie on-line che trattino "Python" filtrando opportunamente i dati per eliminare i riferimenti al Monty Python; oppure forse cercare "Python" e "language".
Attualmente è ad Amsterdam, gentilmente ospitato da XS4ALL. Si ringrazia Thomas Wouters per il lavoro svolto per poter ospitare python.org.
Nello stesso periodo in cui aveva iniziato ad implementare Python, Guido van Rossum stava anche leggendo l'edizione scritta dei copioni del "Circo Volante Monty Python" (nel caso non la conosciate si tratta di una serie di commedie televisive trasmessa dalla BBC negli anni '70). Sentiva la necessità di dare al linguaggio un nome che fosse allo stesso tempo corto, unico e piuttosto misterioso, così decise di chiamarlo Python.
Molto stabile. A partire dal 1991 ad intervalli di 6-18 mesi sono state rilasciate nuove versioni stabili, e sembra che in futuro si procederà allo stesso modo. Attualmente, fra il rilascio di due versioni principali, trascorrono all'incirca 18 mesi.
L'introduzione dei rilasci "retrospettivi" di "bugfix" (NdT. risoluzione bug) ha permesso una notevole stabilità della versione corrente. Le versioni di Bugfix, indicate nel numero di versione mediante un terzo componente, (ad es. 2.1.3, 2.2.2), vengono rilasciate per garantire stabilità; contengono solo la risoluzione di problemi conosciuti, e viene assicurato che, tra il rilascio di una serie di queste versioni, l'interfaccia non subisca alcun cambiamento.
La versione più stabile al momento è la versione 2.4.1.
Probabilmente sono decine di migliaia, sebbene sia difficile stabilirlo con precisione. Python è scaricabile liberamente, per cui non ci sono intermediari, ed è disponibile da diversi siti oltre ad essere incluso in molte distribuzioni Linux, per cui, al riguardo, le statistiche di scarimento non sarebbero significative. La lista di discussione comp.lang.python è molto attiva, ma non tutti gli utilizzatori di Python inviano messaggi al gruppo o li leggono. Oltretutto non esiste un numero preciso dei sottoscrittori, neppure degli utilizzatori di Python.
Si veda http://www.pythonology.org/success: una lista dei progetti che impiegano Python. Consultando le precedenti conferenze su Python si scopriranno i contributi ottenuti da diverse aziende e organizzazioni.
Progetti di ragguardevole importanza che usano Python comprendono the Mailman mailing list manager e the Zope application server. In diverse distribuzioni di Linux, come la più nota Red Hat, il programma di installazione ed amministrazione del sistema è scritto in parte o totalmente in Python. Ci sono aziende che usano al loro interno Python, come Google, Yahoo, e Industrial Light & Magic.
Si veda http://www.python.org/peps: cioè le Python Enhancement Proposals (PEPs) (NdT. Proposte di miglioramento per Python). Si tratta di un modello di documento che descrive gli aspetti di una nuova funzionalità da adottare in Python, fornendone una motivazione ed una breve specifica tecnica. PEP 1 spiega il procedimento ed il formato di una PEP; lo si consulti prima di sottoscriverne una.
Le nuove funzionalità in fase di sviluppo vengono trattate nella lista di discussione python-dev .
Generalmente no. Nel mondo esistono milioni di linee di codice Python, per cui qualunque modifica del linguaggio che renda inutilizzabile anche la più piccola parte di un programma esistente è da disapprovare. Anche se si riesce a fornire un programma di conversione, rimane il problema dell'aggiornamento della documentazione: sono stati scritti molti libri riguardanti Python e non pare conveniente in un colpo solo renderli tutti obsoleti.
E' invece importante, nel caso di necessarie modifiche, fornire un percorso graduale di aggiornamento. Nella PEP 5 sono descritte le procedure da adottare per garantitire la compatibilità con il passato, in modo da ridurre per gli utilizzatori l'impatto del cambiamento.
La Python Software Foundation è un'organizzazione no-profit indipendente che detiene i diritti di copyright dalla versione 2.1 di Python in poi. Lo scopo principale della PSF è di progredire nella tecnologia open-source relativa a Python è di pubblicizzare il suo utilizzo. La home page della PSF la trovate a http://www.python.org/psf/.
Negli Stati Uniti le donazioni devolute alla PSF sono esenti da tasse. Nel caso si utilizzi Python e lo si trovi utile, potete gentilmente contribuire mediante la PSF donation page.
Fino ad Agosto 2003 non sono state riscontrate grosse problematiche: la conformità allo standard Y2K non sembra rappresentare un problema.
Python esegue pochi calcoli sulle date e per eseguire questi si affida alle funzioni della libreria in C. In Python generalmente è difficile che avvengano incompatibilità da anno 2000 perché rappresenta il tempo sia inteso come secondi passati a partire dal 1970 sia come una tupla (anno, mese, giorno, ...) dove l'anno è espresso con quattro cifre. Per cui finché la libreria in C sarà corretta, Python non avrà incompatibilità. Naturalmente può accadere che una particolare applicazione scritta in Python utilizzi l'anno rappresentato da due sole cifre.
Dato che Python è disponibile gratuitamente, non viene fornita alcuna garanzia incondizionata. Se ci sono delle problematiche impreviste, la responsabilità è a carico dell'utilizzatore piuttosto che dello sviluppatore, e non potrete citare nessuno per i derivanti danni. Nella licenza d'uso di Python è contenuta la seguente avvertenza:
[volutamente non tradotta in quanto licenza d'uso NdT]
4. PSF is making Python 2.3 available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.3 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.3, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
La buona notizia consiste nel fatto che se si riscontra un problema, si ha a disposizione gli interi sorgenti per poterlo rintracciare e risolvere. Questo è un vantaggio degli ambienti di programmazione open source.
Certo. Se si volesse discutere dell'uso di Python in campo educativo, allora sarà intessante potersi iscrivere alla lista di discussione the edu-sig mailing list.
E' ancora frequente per gli studenti iniziare da un linguaggio di programmazione procedurale (o suo subset) a tipizzazione statica come il Pascal, C oppure un subset di C++ o Java. Sarebbero trattati meglio insegnandogli Python come primo linguaggio. Python ha una sintassi molto semplice e coerente ed ha una grande libreria standard. Poi, importantissimo, l'uso di Python ad un corso di programmazione per principianti, permette agli studenti di concentrarsi di più su importanti tecniche di programmazione, come la scomposizione dei problemi e la progettazione dei tipi di dati. Gli studenti, mediante Python, possono essere rapidamente introdotti a concetti basilari quali i cicli e le procedure. Durante il primo corso, probabilmente potranno anche riuscire a lavorare con gli oggetti definiti dall'utente.
Ad uno studende che non abbia mai programmato prima, adottare un linguaggio a tipizzazione statica può sembrare anormale. Presenta una complessità addizionale che lo studente deve padroneggiare e che rallenta l'andamento del corso. Gli studenti provano ad imparare a ragionare come fa un computer, scomponendo i problemi, progettando interfaccie che siano coerenti ed incapsulando dati. Anche se, nel lungo periodo, è importante imparare ad usare un linguaggio a tipizzazione statica, non è necessariamente il miglior argomento valido per adottarlo in un corso di programmazione per principianti.
Molti altre caratteristiche di Python lo fanno prediligere come primo linguaggio. Come Java, Python possiede una vasta libreria standard: così è possibile, durante il corso, assegnare rapidamente agli studenti progetti di programmazione che facciano qualcosa. Tali compiti non si riducono al calcolo delle quattro solite funzioni ed ai programmi di controllo del bilancio. Grazie all'utilizzo della libreria standard, gli studenti possono ottenere soddisfazioni lavorando ad applicazioni reali mentre ancora imparano i fondamenti della programmazione. L'impiego della libreria standard insegna agli studenti anche a riutilizzare il codice. Le capacità degli studenti si possono estendere anche con i moduli di terze parti come PyGame.
L'interprete interattivo di Python consente agli studenti di provare gli aspetti del linguaggio mentre stanno programmando. Possono mantenere una finestra con l'interprete in esecuzione mentre inseriscono il codice del loro programma in un'altra. Se non si ricordano i metodi di una lista, possono sempre fare come segue:
>>> L = [] >>> dir(L) ['append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> help(L.append) Help on built-in function append: append(...) L.append(object) -- append object to end >>> L.append(1) >>> L [1]
Grazie all'interprete, la documentazione rimarrà sempre a portata di mano dello studente mentre programma.
Inoltre esistono molti validi ambienti di sviluppo grafici (IDE) per Python. IDLE è un ambiente multi-piattaforma scritto in Python mediante Tkinter. Per Windows esiste un IDE specifico: PythonWin. Gli utenti di Emacs saranno contenti di sapere che esiste una modalità Python. Tutti questi ambienti di programmazione prevedono l'evidenziazione della sintassi, l'indentazione automatica e l'accesso mentre si programma all'interprete interattivo. Si consulti http://www.python.org/editors/ per avere una lista completa degli ambienti di sviluppo per Python.
A partire dalla versione 2.3 di Python, la distribuzione contiene il PyBSDDB package <http://pybsddb.sf.net/> che rimpiazza il vecchio modulo bsddb. Contiene le funzioni che forniscono la compatibilità a livello API con il passato, ma richiede una nuova versione della sottostante libreria Berkeley DB.I file che erano stati creati con il vecchio modulo bsddb non possono essere aperti direttamente dal nuovo modulo.
L'utente può convertire nel nuovo formato i vecchi file di database che possiede utilizzando un paio di script compresi in Python 2.3: db2pickle.py e pickle2db.py situati nella cartella Tools/scripts. Basta usare la vecchia versione di Python ed eseguire lo script db2pickle.py per la conversione del database in un file in formato pickle. Ad esempio:
python2.2 <pathto>/db2pickley.py database.db database.pck
Si rinomini il vecchio database:
mv database.db olddatabase.db
Adesso si converta il file pickle nel nuovo formato di database:
python2.3 <pathto>/pickle2db.py database.db database.pck
La corrispondenza dei comandi che userete possono variare leggermente a seconda della vostra installazione. Per maggiori dettagli sul funzionamento di questi due script si controlli la stringa doc presente all'inizio di essi.
Guido van Rossum ritiene che l'utilizzo dell'indentazione per raggruppare le istruzioni sia estremamente elegante e contribuisca molto alla chiarezza dei programmi Python. La maggior parte delle persone imparano ad amare questo aspetto dopo un poco.
Dal momento che non ci sono parentesi di inizio/fine non si può presentare alcuna discordanza fra i gruppi di istruzioni percepite dal parser e quelle lette dall'utente. A volte i programmatori in C incontrano porzioni di codice simili a questa:
if (x <= y) x++; y--; z++;
Se la condizione è vera (true) viene eseguita solo l'istruzione x++, ma l'indentazione induce a supporre diversamente. Anche i programmatori esperti in C rimangono qualche volta a fissare tale codice per molto tempo chiedendosi perchè y viene diminuito anche per x > y.
Dato che non ci sono parentesi di inizio/fine, Python è molto meno incline ai conflitti dovuti allo stile del codice. In C ci sono diverse maniere di porre le parentesi graffe. Se siete abituati a leggere e scrivere il codice che utilizzi un determinato stile, vi sentirete almeno piuttosto a disagio quando leggerete (o vi sarà richiesto di scrivere) in un altro stile.
Molti stili nell'uso del codice mettono le parentesi di inizio/fine da soli in una riga. Ciò rende i programmi piuttosto lunghi e spreca un evidente spazio utile dello schermo, rendendo difficile ottenere una buona visione d'insieme del programma. La funzione ideale dovrebbe stare in un una schermata (diciamo 20-30 linee). 20 linee di codice Python rendono molto di più di 20 linee di C. Questo è dovuto non solo alla mancanza delle parentesi di inizio/fine: incidono anche la mancanza delle dichiarazioni e dei tipi di dati di alto livello; la sintassi basata sull'indentazione sicuramente aiuta.
Spesso gli utenti sono veramente sorpresi da risultati come questo:
>>> 1.2-1.0 0.199999999999999996
e pensano si tratti di un bug presente in Python. Non lo è. E' un problema causato dalla rappresentazione interna dei numeri a virgola mobile, che usano un numero fisso di cifre binarie per rappresentare un numero decimale. Alcuni numeri decimali non possono essere rappresentati esattamente in binario, provocando piccoli errori di arrotondamento.
In matematica frazionaria, ci sono molti numeri che non possono essere rappresentati con un numero fisso di cifre decimali, ad esempio 1/3 = 0.3333333333.......
In base 2, 1/2 = 0.1, 1/4 = 0.01, 1/8 = 0.001, ecc. .2 equivale 2/10 che equivale a 1/5, che risulta nel numero binario frazionario di 0.001100110011001...
I numeri a virgola mobile hanno solo 32 o 64 bit di precisione, per cui le cifre ad un certo punto vengono tagliate ed il numero risultante in decimale diventa 0.199999999999999996 e non 0.2.
La funzione di rappresentazione dei numeri a virgola mobile repr() stampa quante cifre sono necessarie per far eguagliare a vero (true) la funzione eval(repr(f)) == f per ogni numero a virgola mobile f. La funzione str() stampa meno cifre e questo spesso porta al più sensato numero che si voleva ottenere:
>>> 0.2 0.20000000000000001 >>> print 0.2 0.2
Di nuovo, questo non c'entra niente con Python, ma è dovuto a come la sottostante piattaforma in C gestisce i numeri a virgola mobile, ed infine alla imprecisione che si otterrà sempre quando si vogliono scrivere dei numeri come stringa o con un numero fisso di cifre.
Una delle conseguenze di tutto ciò è che risulta pericoloso comparare il risultato di un calcolo con un numero a virgola mobile mediante == ! Piccole imprecisioni portano al fallimento del confronto ==. Invece, bisogna controllare che la differenza tra i due numeri sia minore di una certa soglia:
epsilon = 0.0000000000001 # L'errore più piccolo ammesso expected_result = 0.4 if expected_result-epsilon <= computation() <= expected_result+epsilon: ...
Per maggiori informazioni si veda il capitolo sui floating point arithmetic nel tutorial di Python.
Ci sono diversi vantaggi.
Le prestazioni: sapere che una stringa è invariabile rende più facile la loro definizione in fase progettuale: i requisiti di memorizzazione sono fissi e non cambiano. Questa è anche una delle ragioni della distinzione esistente fra tuple e liste.
L'altro vantaggio è che le stringhe in Python sono considerate "elementari" come i numeri. Come nessun tipo di attività potrà cambiare il valore 8 in qualcosa di diverso, allo stesso modo in Python nessuna operazione potrà variare la stringa "otto" in qualcos'altro.
L'idea è stata presa da Modula-3. E' risultata molto utile, per una serie di motivi.
Primo, è molto più ovvio che si stia usando un metodo o un attributo d'istanza e non una variabile locale. Leggere self.x oppure self.meth() rende assolutamente chiaro che si sta utilizzando una variabile d'istanza o un metodo, anche se non si conosce il contenuto della definizione di classe. In C++ si può scegliere di avvisare dell'assenza di una dichiarazione delle variabili locali (assumendo che le globali sono poco frequenti oppure facilmente riconoscibili) -- ma in Python, non ci sono dichiarazioni di variabili locali, per cui per esserne sicuri si deve guardare la definizione di classe. Alcuni standard di codifica presenti in C++ e Java assumono che la chiamata degli attributi d'istanza deve avere il prefisso m_ , per cui diventa utile anche in questi linguaggi l'uso esplicito.
Secondo, significa che non è necessario l'uso di alcuna sintassi speciale se si vuole referenziare in maniera esplicita oppure chiamare il metodo da una determinata classe. In C++, se si vuole utilizzare un metodo a partire da una classe di base che è stata sovrascritta in una classa derivata, si deve usare :: come operatore. In Python si può scrivere baseclass.methodname(self, <lista di argomenti>). Questo è particolarmente utile per i metodi __init__(), ed in generale nei casi dove un metodo di una classe derivata vuole estendere il metodo della classe di base con lo stesso nome e poi deve in qualche modo chiamare il metodo della classe di base.
Infine, risolve un problema di sintassi negli assegnamenti di variabili d'istanza: siccome in Python le variabili locali sono (per definizione!) quelle variabili aventi un valore assegnato loro nel corpo di una funzione (e che non sono state dichiarate esplicitamente globali), ci deve essere un modo per dire all'interprete che l'assegnamento deve intendersi compiuto ad una variabile d'istanza invece di una variabile locale, e questo (per ragioni di efficienza) deve avvenire preferibilmente a livello di sintassi. Il C++ permette tutto ciò mediante le dichiarazioni, ma Python non ha le dichiarazioni e sarebbe un peccato introdurle solamente per questo scopo. Tutto viene risolto mediante l'uso esplicito di "self.var". Allo stesso modo, per l'utilizzo di variabili d'istanza, dover scrivere "self.var" significa che i riferimenti a nomi non qualificati all'interno di un metodo non deve cercare le directory d'istanza. Per dirlo in altra maniera, le variabili locali esistono in due differenti namespace [spazio dei nomi NdT], per cui bisogna dire a Python quale utilizzare.
Molti utenti abituati al C o Perl lamentano la mancanza di questa sintassi:
while (line = readline(f)) { ...fai qualcosa con line... }
mentre in Python si è costretti a scrivere questo:
while True: line = f.readline() if not line: break ...fai qualcosa con line...
La ragione per cui non sono permessi gli assegnamenti nelle espressioni in Python è dovuta ad un comune bug, difficile da trovare nei suddetti linguaggi, provocato da questo costrutto:
if (x = 0) { ...gestione degli errori... } else { ...codice che funziona solo per x diverso da zero... }
L'errore è semplicemente di battitura: è stato scritto x = 0, che assegna 0 alla variabile x, mentre sicuramente ciò che si voleva intendere è il confronto x == 0.
Sono state proposte diverse alternative. Molte sono banalità che risparmiano qualche battitura ma usano una sintassi arbitraria o incomprensibile oppure parole chiavi, e non assolvono il criterio di semplicità che debbono avere le proposte di modifica di un linguaggio: ciò intuitivamente suggerisce il significato appropriato ad un lettore che non ha ancora affrontato tale costrutto.
Un fenomeno interessante è che la maggioranza dei programmatori che hanno sperimentato Python riconoscono l'espressione "while True" e non ne risentono più della mencanza dell'assegnamento nel costrutto dell'espressione. Solo i nuovi utenti esprimono il forte desiderio di aggiungerlo al linguaggio.
C'è un modo alternativo per scrivere ciò che sembra molto attraente ma è generalmente meno robusto della soluzione di "while True":
line = f.readline() while line: ...fai qualcosa con line... line = f.readline()
Il problema risiede nel fatto che se viene in mente di cambiare la modalità per ottenere la successiva riga (ad esempio si vuole modificarla in sys.stdin.readline()) ci si deve ricordare di modificare due passi nel programma: la seconda occorrenza è celata in fondo al ciclo.
Il miglior approcio consiste nell'utilizzo degli iteratori, rendendo possibile ciclare attraverso gli oggetti usando l'istruzione for. Per esempio, nella versione corrente di Python gli oggetti file supportano il protocollo iterativo, così adesso si può scrivere semplicemente:
for line in f: ... fai qualcosa con line...
Il motivo più importante risiede nella storia di Python. Le funzioni venivano usate per quelle operazioni intese come generiche per un insieme di tipi di dati e che inoltre potessero lavorare con gli oggetti che non possedevano alcun metodo (ad esempio le tuple). Inoltre può essere conveniente avere una funzione prontamente applicabile ad una collezione amorfa di oggetti quando si vogliano utilizzare certi aspetti funzionali di Python (map(), apply() ed altro).
Infatti l'implementazine di len(), max(), min() come funzioni incorporate ha richiesto meno codice rispetto alla loro implementazione come metodi per ogni tipo. Uno può obiettare presentando casi specifici ma oramai tale implementazione forma una parte di Python ed è troppo tardi per compiere adesso modifiche di tale rilevanza. Le funzioni devono rimanere per evitare di rendere inutilizzabile molto del codice gia' scritto.
Si noti che per le operazioni sulle stringhe, Python è passato dall'uso di funzioni esterne (il modulo string) ai metodi. Comunque, len() rimane ancora una funzione.
A partire dalla versione 1.6 di Python le stringhe sono diventate molto più simili agli altri tipi standard, quando sono stati aggiunti metodi che fornivano le stesse funzionalità che sono state sempre disponibili utilizzando le funzioni del modulo string. Molti di questi metodi nuovi sono stati ampiamente accettati, ma l'unico che fa sentire a disagio molti programmatori è:
", ".join(['1', '2', '4', '8', '16'])
che dà come risultato:
"1, 2, 4, 8, 16"
Esistono due obiezioni a tale impiego.
La prima consiste in questa frase: "Sembra davvero brutto usare un metodo per una costante di stringa!". La risposta è che può essere, ma una costante di stringa è solo un valore costante. Se ai metodi viene premesso di legare nomi alle stringhe, non c'è nessuna ragione logica per non renderli disponibili anche per le costanti di stringa.
La seconda obiezione viene in genere buttata lì come: " Sto veramente dicendo ad una sequenza di riunire assieme i suoi membri mediante una costante di stringa?". Ti stai sbagliando. Per alcune ragioni sembra meno difficile avere split() come metodo di stringa, dal momento che in quel caso è facile vederlo come
"1, 2, 4, 8, 16".split(", ")
un'istruzione di una costante di stringa che restituisce sottostringhe delimitate dal dato separatore (oppure, predefinito, un numero arbitrario di spazi bianchi). In questo caso una stringa Unicode restituisce una lista di stringhe Unicode, mentre una stringa ASCII restituisce una lista di stringhe ASCII, così abbiamo accontentato tutti.
join() è un metodo di stringa perchè nel suo impiego bisogna dirgli il separatore di stringa che deve iterare su di una certa sequenza, andando così a formare una rappresentazione in stringhe di ciascuno degli elementi della sequenza ed inserendo tra quest'ultimi il separatore stesso. Questo metodo può essere utilizzato con ogni argomento che rispetta le regole di una sequenza di oggetti, inclusa ognuna delle nuove classi che l'utente potrebbe definire.
A causa del fatto che si tratta di un metodo di stringa, è in grado di lavorare sia con stringhe Unicode che stringhe ASCII. Se dei tipi di sequenza avevano join() come metodo, allora essi devono decidere che tipo di stringa devono restituire a seconda del tipo di separatore.
Se non vi ha convinto nessuna di queste motivazioni, allora potete continuare ad usare la funzione join() del modulo string, che vi permette di scrivere
string.join(['1', '2', '4', '8', '16'], ", ")
Un blocco try/except è estremamente efficiente. In effetti e' eseguire l'eccezione che risulta dispendioso. Nelle versioni di Python precedenti alla 2.0 si utilizzava spesso questa sintassi:
try: value = dict[key] except KeyError: dict[key] = getvalue(key) value = dict[key]
Questo ha senso solamente nel caso ci si attenda che dict contenga key nella maggioranza dei casi. In caso contrario, il codice piu' efficente e':
if dict.has_key(key): value = dict[key] else: dict[key] = getvalue(key) value = dict[key]
(Nella versione di Python 2.0 o successiva, si può utilizzare value = dict.setdefault(key, getvalue(key)).)
E' possibile avere lo stesso risultato abbastanza facilmente mediante una sequenza di if... elif... elif... else. Ci sono state alcuni propositi per l'uso nella sintassi dell'istruzione switch, ma non c'è stato (ancora) consenso su come e quando fare una serie di test d'impiego. Si veda la PEP 275 per maggiori dettagli e conoscere la situazione attuale.
Nei casi in cui si abbia la necessità di scegliere fra un enorme numero di possibilità, si può creare un dizionario che mappi i valori dei singoli casi come funzioni da chiamare. Per esempio:
def function_1 (...): ... functions = {'a': function_1, 'b': function_2, 'c': self.method_1, ...} func = functions[value] func()
Per metodi che effettuino chiamate su oggetti, si può semplicemente ricorrere all'uso del getattr() incorporato per richiamare metodi con un nome particolare:
def visit_a (self, ...): ... ... def dispatch (self, value): method_name = 'visit_' + str(value) method = getattr(self, method_name) method()
Si suggerisce l'impiego di un prefisso per i nomi dei metodi, come il visit_ di questo esempio. Senza l'uso di tale prefisso, se i valori dovessero provenire da una sorgente di natura incerta, un malintenzionato potrebbe essere in grado di chiamare ogni metodo dell'oggetto.
Risposta 1: Sfortunatamente, l'interprete sottopone almeno un frame di stack C per ogni frame di stack Python. Inoltre le estensioni possono effettuare chiamate di ritorno verso Python in momenti praticamente casuali. Quindi una implementazione completa dei threads richiederebbe il loro supporto da parte del C.
Risposta 2: Per fortuna, esiste una Stackless Python, che ha la routine dell'interprete completamente riscritta in modo da evitare lo stack del C. E' ancora allo stato sperimentale ma sembra molto promettente. Sebbene sia compatibile a livello binario con lo standard Python, è ancora poco chiaro se sia il caso di includerlo nella sua struttura -- potrebbe essere un po' troppo rivoluzionario.
I costrutti lambda non possono contenere istruzioni perchè il framework sintattico di Python non è puo' gestire le istruzioni all'interno di espressioni. Comunque, in Python, questo non costituisce un problema serio. Diversamente dai costrutti lambda che in altri linguaggi servono per aggiungere funzionalità, in Python sono solamente una notazione abbreviata usata dall'utente troppo pigro di definire una funzione.
Le funzioni costituiscono già la classe più importante di oggetti di Python, e possono essere dichiarate in ambito locale. Quindi, l'unico vantaggio dell'uso dei costrutti lambda al posto di una funzione definita localmente consiste nel fatto che non si ha bisogno di inventare il nome della funzione ma solamente una variabile locale alla quale viene associato l'oggetto della funzione (che è esattamente lo stesso tipo di oggetto reso dal costrutto lambda)!
Non in modo semplice. In Python, l'insieme dei tipi di dati di alto livello, la tipizzazione dinamica degli oggetti e l'invocazione a livello di run-time dell'interprete (usando eval() oppure exec) comportano che un programma "compilato" consista principalmente in una serie di chiamate all'interno del run-time di Python, anche per operazioni apparentemente semplici come x+1.
Diversi progetti documentati nel newsgroup di Python oppure in passate conferenze su Python hanno mostrato che, sebbene fattibile, le prestazioni raggiunte siano modeste (ad esempio 2x). Jython utilizza la stessa strategia per la compilazione in bytecode Java. (Jim Hugunin ha dimostrato con alcuni programmi demo che, in combinazione con l'analisi dell'intero programma, sono possibili incrementi di prestazioni dell'ordine di 1000 volte. Si vedano per maggiori informazioni i resoconti della Conferenza 1997 su Python .)
Internamente, il codice sorgente Python viene sempre convertito nella rappresentazione in bytecode, e questo viene dopo eseguito nella macchina virtuale di Python. Al fine di evitare la ripetizione generale di analisi e conversione di moduli che variano raramente, tale bytecode viene scritto in un file con estensione ".pyc" ogni qual volta un modulo viene analizzato. Quando il rispettivo file .py cambia, viene nuovamente analizzato e convertito ed il file .pyc viene riscritto.
Non c'è nessuna differenza a livello di prestazioni una volta che il file .pyc è stato caricato, come del resto il bytecode letto dal file .pyc è lo stesso di quello creato dalla conversione diretta. L'unica differenza è che il caricamento di codice dal file .pyc è più veloce dell'analisi e conversione di un file .py, per cui la presenza di file .pyc precompilati migliora il tempo di avvio degli script Python. Se si desidera, il modulo Lib/compileall.py può essere impiegato per la creazione di corretti file .pyc per un dato quantitativo di moduli.
Si noti che lo script principale eseguito da Python, anche se il suo nome finisce in .py, non viene compilato in un file .pyc. Viene compilato in bytecode, ma tale bytecode non viene salvato su di un file. Di solito la maggioranza degli script è piuttosto breve, per cui non causa svantaggi prestazionali.
Ci sono anche diversi programmi che facilitano la fusione di codice Python e C in diverse maniere al fine di migliorare le prestazioni. Per esempio si veda Psyco, Pyrex, PyInline, Py2Cmod, e Weave [Il link corretto e' Weave NdT].
I dettagli della gestione della memoria di Python dipendono dall'implementazione. L'implementazione C standard utilizza dei contatori di riferimento per determinare gli oggetti non accessibili ed un altro meccanismo per raccogliere i riferimenti circolari, che esegue un algoritmo che cerca i riferimenti circolari non accessibili e cancella gli oggetti contenuti. Il modulo gc fornisce le funzioni per eseguire la garbage collection [rimozione dalla memoria degli oggetti non piu' utilizzati, con una traduzione letterale sarebbe la raccolta della spazzatura NdT], ottenere le statistiche di debug ed effettuare il tuning dei parametri relativi.
Jython si appoggia al runtime Java e percio' utilizza il garbage collector della JVM. Questa differenza puo' causare alcuni sottili problemi di porting se il codice Python dipende dal comportamento dell'implementazione dei contatori di riferimento.
Alcune volte gli oggetti restano bloccati temporaneamente nei traceback e quindi non vengono disallocati quando uno se lo aspetta. Per pulire i traceback:
import sys sys.exc_clear() sys.exc_traceback = sys.last_traceback = None
I traceback sono utilizzati nel riportare gli errori, nell'implementazione dei debugger e simili. Contengono un porzione dello stato del programma estratto durante il trattamento di un eccezione (tipicamente dell'eccezione piu' recente).
In assenza di cicli e di traceback i programmi Python non hanno bisogno di gestire in modo esplicito la memoria.
Perche' Python non utilizza uno schema di garbage collection piu' tradizionale? Per un motivo, non e' una funzionalita' standard del C e quindi non e' portabile. (Si, sappiamo della libreria Boehm GC. Ha qualche bit di codice assembler per la maggior parte delle piattaforme, ma non per tutte, e benche' sia per la maggior parte trasparente, non e' completamente trasparente; sono necessarie delle patch per farla funzionare con Python.)
Il GC tradizionale diventa anche anche un problema quando Python viene incluso in altre applicazioni. Mentre in un Python standalone si possono rimpiazzare i richiami malloc() e free() standard con le versioni fornite dalla libreria GC, un'applicazione che contiene Python puo' volere utilizzare i suoi sostituti per malloc() e free() e non quelli di Python. Attualmente Python lavora con qualunque libreria che implementi malloc() e free() correttemente.
In Jython, il seguente codice (che funziona correttamente in CPython) probabilmente esaurira' il numero di file descriptor prima di esaurire la memoria:
for file in <very long list of files>: f = open(file) c = f.read(1)
Utilizzando i contatori di riferimenti e lo schema dei distruttori, ogni nuova assegnazione ad f chiude il file precedente. Utilizzando un GC questo non e' garantito. Se si vuole scrivere un codice che operi correttamente con ogni implementazione di Python e' necessario chiudere in modo esplicito il file; l'esempio seguente funziona qualunque sia il GC:
for file in <very long list of files>: f = open(file) c = f.read(1) f.close()
Gli oggetti referenziati dal namespace globale dei moduli Python non sono sempre deallocati quanto Python termina. Questo puo' avvenire quando vi sono riferimenti circolari. Ci sono anche alcune piccole allocazioni della libreria C che e' impossibile liberare (e.g. un tool come Purify li rivelerebbe). Tuttavia Python e' piuttosto aggressivo nel liberare memoria in uscita e cerca di distruggere ogni singolo oggetto.
Se si vuole forzare Python a cancellare alcuni oggetti al momemento della disallocazione si deve utilizzare l'aggancio sys.exitfunc() per eseguire una funzione che forzi le cancellazioni.
Le liste e le tuple, benche' simili da molti punti di vista, sono generalmente utilizzate in modi fondamentalmente differenti. Le tuple possono essere paragonate ai record del pascal o alle strutture del C; sono piccole collezioni di dati correlati, che possono essere di tipo differente, e su cui su opera come su un gruppo. Ad esempio una coordinata cartesiana e' rappresentata in modo appropriato da una tupla di due o tre numeri.
Le liste, invece, sono piu' simili agli array in altri linguaggi. Tendono ad avere un numero variabile di oggetti che hanno tutti lo stesso tipo e che vengono utilizzati ad uno ad uno. Ad esempio os.listdir('.') restituisce una lista di stringhe che rappresentano l'elenco dei file nella directory corrente. Le tuple sono immutabili, quindi una volta che una tupla e' stata creata non e' possibile cambiare il valore di uno dei suoi elementi. Le liste sono modificabili, quindi e' sempre possibile cambiare un elemento della lista. Solo elementi immutabili possono essere utilizzati come chiavi del dizionario e quindi solo tuple, e mai liste, possono essere utilizzate come chiavi.
Le liste Python sono array di lunghezza variabile, non linked list come in Lisp. L'implementazione utilizza un array contiguo di riferimenti ad altri oggetti, e mantiene un puntatore a questo array e la lunghezza dell'array nella struttura di testa della lista.
Questo rende l'indirizzamento della lista a[i] un'operazione il cui costo e' indipendente dalla dimensione della lista o dal valore dell'indice.
Quando nuovi elementi sono inseriti nella lista o al fondo, l'array di riferimenti viene ridimensionato. Per rendere piu' efficenti inserimenti sucessivi viene allocato dello spazio ulteriore in modo che, per alcune volte, non sia necessario un ridimensionamento.
I dizionari Python sono implementati come tabelle di hash ridimensionabili. Rispetto i B-tree questo da' migliori prestazioni in ricerca (di gran lunga l'operazione piu' frequente) nella maggior parte delle circostanze e l'implementazione e' piu' semplice. I dizionari necessitano di un codice di hash calcolato utilizzando la funzione interna hash() per ogni chiave memorizzata sul dizionario. Il codice di hash cambia notevolmente al cambiare della chiave; ad esempio per "Python" il codice di hash e' -539294296 mentre per "python", una stringa che cambia di un solo bit, il codice di hash e' 1142331976. Il codice di hash e' utilizzato per calcolare una posizione in un array interno su cui il valore verra' memorizzato. Assumendo che si utilizzino chiavi tutte con differenti codici di hash, questo significa che la ricerca di una chiave su un dizionario richiede un tempo costante: O(1), in notazione informatica. Significa anche che non viene mantenuto alcun elenco ordinato delle chiavi e che il percorrere l'insieme delle .keys() e degli .items() restituisce il contenuto del dizionario in un simpatico disordine.
L'implementazione dei dizionari come tabelle di hash utilizza un codice di hash basato sulla chiave per ritrovare i dati. Se la chiave fosse un oggetto modificabile il suo valore potrebbe cambiare e quindi anche il codice di hash. Ma poiche' per qualunque ragione si modifichi il valore dell'oggetto chiave non e' possibile sapere se questa e' stata utilizzata come chiave del dizionario, non verrebbe spostato l'elemento all'interno del dizionario. Ricercando lo stesso oggetto nel dizionario questo non verrebbe ritrovato poiche' il codice di hash sarebbe differente. Provando a cercare con il vecchio valore, non verrebbe trovato comunque, perche' il valore dell'oggetto trovato nel contenitore hash sarebbe differente.
Se si vuole un dizionario basato su una lista basta semplicemente convertire la lista in una tupla; la funzione tuple(L) crea una tupla con gli stessi elementi della lista L. Le tuple sono immutabili e quindi possono essere usate come chiavi del dizionario.
Sono state proposte alcune soluzioni inaccettabili:
Effettuare l'hash delle liste utilizzando il loro indirizzo (object ID). Questo non andrebbe bene poiche' costruendo una nuova lista con gli stessi valori l'oggetto non verrebbe trovato: e.g.:
d = {[1,2]: '12'} print d[[1,2]]
solleverebbe un'eccezione KeyError poiche' l'id di [1,2] utilizzato nella seconda linea differisce da quello della prima linea. In altre parole, le chiavi del dizionario dovrebbero essere confrontate utilizzando ==, e non utilizzando 'is'.
Effettuare una copia quando si utilizza una lista come chiave. Non funzionerebbe perche' la lista, essendo un oggetto mutabile, potrebbe contenere un riferimento a se stessa ed il codice per la copia potrebbe finire in un loop infinito.
Permettere le liste come chiavi ma dire agli utenti di non modificarle. Questo permetterebbe un tipo di errori difficili da tracciare quando le chiavi venissero modificate per dimenticanza o per errore. Inoltre invaliderebbe un'importante proprieta' invariante dei dizionari: ogni valore in d.keys() e' utilizzabile come chiave del dizionario.
Marcare le liste come read-only quando vengono utilizzare come chiavi del dizionario. Il problema e' che solo l'oggetto top-level che puo' cambiare; e' possibile utilizzare come chiave una tupla contenente una lista. Utilizzare qualunque cosa come chiave in un dizionario richiederebbe di segnare come read-only tutti gli oggetti raggiungibili -- e nuovamente oggetti con riferimenti a se stessi causerebbero un loop infinito.
C'e' un trucco per aggirare la cosa se e' necessario, ma da usare a proprio rischio e pericolo: e' possibile effettuare un wrap di una struttura mutevole su una classe d'istanza che abbia i metodi __cmp__ e __hash__. E' necessario fare in modo che tali oggetti di wrap che risiedono nel dizionario (o in un'altra struttura basata sull'hash), restino fissi finche' l'oggetto e' nel dizionario (o in un altra struttura analoga):
class ListWrapper: def __init__(self, the_list): self.the_list = the_list def __cmp__(self, other): return self.the_list == other.the_list def __hash__(self): l = self.the_list result = 98767 - len(l)*555 for i in range(len(l)): try: result = result + (hash(l[i]) % 9999999) * 1001 + i except: result = (result % 7777777) + i * 333 return result
Deve essere notato che il calcolo dell'hash e' complicato dalla possibilita' alcuni membri della lista non possano essere utilizzati nel calcolo dell'hash e dalla possibilita' di un overflow aritmetico.
Inoltre deve essere vero che se o1 == o2 (ie o1.__cmp__(o2)==0) allora hash(o1)==hash(o2) (ie, o1.__hash__() == o2.__hash__()), che l'oggetto sia nel dizionario o meno. Se queste condizioni non sono soddisfatte i dizionari e le altre strutture ad hash non funzionano correttamente.
Nel caso di ListWrapper, ogni volta che l'oggetto wrap e' nel dizionario la lista non deve essere cambiata per non avere anomalie. Non deve essere fatto a meno di non essere preparati a pensare a lungo sulle condizioni e sulle conseguenze di non averle soddisfatte correttamente. Vi abbiamo avverti!
Nei casi in cui le prestazioni hanno importanza, effettuare una copia di una lista solo per ordinarla sarebbe uno spreco. Quindi list.sort() ordina la lista stessa. In modo da ricordare tale fatto, non restituisce la lista ordinata. In questo modo non puo' capitare di sovrascrivere una lista quando sono necessari sia la lista ordinata che la lista originale.
In Python 2.4 e' stata aggiunta una nuova funzione interna: sorted(). Questa funzione crea una nuova lista dal parametro iterativo, la ordina e la restituisce.
Come risultato ecco il modo di scandire le chiavi di un dizionario in ordine sequenziale:
for key in sorted(dict.iterkeys()): ...do whatever with dict[key]...
Nelle versioni di Python precedenti alla 2.4 la sintassi corretta e' la seguente:
keys = dict.keys() keys.sort() for key in keys: ...do whatever with dict[key]...
La specifica di un'interfaccia per un modulo cosi' come presente in linguaggi come il C++ e Java descrive un prototipo per il metodo e per le funzioni del metodo. Alcuni pensano che la verifica in compilazione dell'interfaccia aiuti nella costruzione di programmi di grandi dimensioni. Python non supporta la specifica di interfacce in modo diretto, ma molti dei loro vantaggi possono essere ottenuti con una corretta verifica dei componenti, che puo' essere eseguita molto facilmente in Python. E' anche disponibile un tool, PyChecker, che puo' essere utilizzato per trovare problemi dovuti alle sottoclassi.
Un buon insieme di test per un modulo puo' essere contemporaneamente un test di non-regressione, una specifica di interfaccia del modulo e fornire esempi di utilizzo. Molti moduli Python possono essere eseguiti come script per fornire un semplice "self test." Anche moduli con interfacce esterne complesse spesso possono essere testati singolarmente con semplici "stub" in emulazione di interfacce esterne. I moduli doctest e unittest o framework di terze parti possono essere utilizzati per costruire un insieme esuastivo di test che percorrono ogni linea di codice di un modulo.
Un'appropriata metodologia di test aiuta a costruire applicazioni di grandi dimensione e complesse in Python cosi' come farebbe una specifica di intefacce. In effetti puo' produrre risultati migliori poiche' la specifica di intefacce non puo' controllare alcune proprieta' dei programmi. A esempio il metodo append() aggiunge nuovi elementi alla fine di una qualche lista interna; una specifica di interfaccia non puo' controllare che l'implementazione di append() sia corretta, ma e' il controllo di questa proprieta' con un test e' banale.
Scrivere un insieme di test e' molto utile ed e' opportuno disegnare il codice tenendo presente che deve essere facilmente testato. Una delle tecniche sempre piu' popolari e' quella dello sviluppo test-directed (guidato dai test) che prevede di scrivere le parti di test ancora prima di scrivere il codice finale. Naturalmente Python consente di essere pigri e di non scrivere alcun test.
E' possibile utilizzare le eccezioni per realizzare un "goto" strutturato che opera anche tra richiami di funzione. Le eccezioni possono realizzare in modo conveniente tutti gli utilizzi ragionevoli dei costrutti "go" e "goto" del C, Fortran e di altri linguaggi. Ad esempio:
class label: pass # declare a label try: ... if (condition): raise label() # goto label ... except label: # where to goto pass ..
Questo non consente di saltare all'interno di un loop che e' comunque considerato un abuso del goto in ogni caso. Da usare con moderazione.
Questa e' una limitazione implementativa dovuta al modo estremamente semplice in cui Python genera il bytecode. Il blocco try effettua una push di qualcosa sullo "block stack" che il continue dovrebbe togliere nuovamente. L'attuale generatore di codice non ha le strutture dati necessarie e cosi' il continue non e' in grado di generare il codice corretto.
Deve essere notato che Jython non ha questa restrizione!
Piu' precisamente non possono terminare con un numero dispari di backslash: il backslash dispari alla fine effettua un escape del carattere di chiusura della stringa lasciandola non terminata.
Le raw strings sono state disegnate per semplificare la creazione dell'input a processori (tipicamente motori di riconoscimento delle espressioni regolari) che debbono fare una loro gestione degli escape del backslash. Tali processori considererebbero un backslash finale come un errore in ogni caso, cosi' le raw strings non lo permettono. In cambio esse permettono di passare il carattere di delimitazione delle stringhe utilizzando l'escape con il backslash. Queste regole funzionano senza problemi quando le raw strings sono utilizzate per l'uso che era stato loro previsto.
Se si cerca di construire un pathname Windows, deve essere notato che tutte le versioni di Windows accettano anche gli slash:
f = open("/mydir/file.txt") # works fine!
Se si cerca di costruire un pathname per un comando DOS si puo' provare uno dei seguenti statement:
dir = r"\this\is\my\dos\dir" "\\" dir = r"\this\is\my\dos\dir\ "[:-1] dir = "\\this\\is\\my\\dos\\dir\\"
Perche' tale costrutto sarebbe ambiguo.
Alcuni linguaggi, come l'Object Pascal, Delphi, and C++, utilizzano tipi statici. Cosi' e' possibile sapere, in modo non ambiguo, quale membro viene assgnato con la clausola "with". Questo e' il punto principale. Il compilatore conosce sempre lo scope di ogni variabile al momento della compilazione.
Python utilizza tipi dinamici. E' impossibile sapere in anticipo quale attributo verra' referenziato a runtime. Gli attributi membro possono essere aggiunti o eliminati dagli oggetti in qualsiasi momento. Questo rende impossibile conoscere, con una semplice lettura, quale attributo venga referenziato - un attributo locale, globale o membro.
Ad esempio, nel seguente frammento:
def foo(a): with a: print x
Il tratto di codice assume che "a" deve avere un attributo membro chiamato "x". Tuttavia non c'e' nulla in Python che lo garantisca. Cosa succederebbe se "a" fosse, per esempio, un intero? E se ci fosse una variabile chiamata "x", verrebbe utilizzata all'interno del blocco with? La natura dinamica di Python rende difficili questo tipo di scelte.
Il vantaggio principale di "with" e di funzionalita' simili di un linguaggio (la riduzione del codice) puo' tuttavia essere facilmente ottenuta in Python con un'assegnazione. Invece di:
function(args).dict[index][index].a = 21 function(args).dict[index][index].b = 42 function(args).dict[index][index].c = 63
basta scrivere:
ref = function(args).dict[index][index] ref.a = 21 ref.b = 42 ref.c = 63
Questo ha anche l'effetto collaterale di incrementare la velocita' di esecuzione poiche' il name binding viene risolto a runtime in Python, e la seconda versione necessita di tale risoluzione una sola volta. Se l'oggetto referenziato non avesse gli attributi a,b o c il risultato naturalmente sarebbe sempre un'eccezione a run-time.
I due punti sono richiesti soprattutto per migliorare la leggibilita' del codice (uno dei risultati del linguaggio sperimentale ABC). Considerando:
if a==b print a
rispetto a:
if a==b: print a
Il secondo esempio e' un poco piu' facile da leggere. Inoltre l'uso dei due punti introduce l'esempio nella seconda linea di questa risposta alla FAQ; e' il modo normale di scrivere in Inglese.
Un'altra ragione, di minore importanza, e' che i due punti rendono piu' semplici gli editor con suggerimenti sintattici; l'editor deve solo controllare la presenza dei due punti per decidere quando l'indentazione deve essere incrementata anziche' dover effettuare un parsing piu' complesso del testo del programma.