2010-08-06 19 views
11

Come tutti sappiamo ormai (spero), Python 3 sta lentamente iniziando a sostituire Python 2.x. Naturalmente ci vorranno molti MOLTI anni prima che la maggior parte del codice esistente venga finalmente trasferita, ma ci sono cose che possiamo fare adesso nel nostro codice versione 2.x per rendere più semplice l'uso dell'interruttore.Prepararsi a convertire da Python 2.x a 3.x

Ovviamente dare un'occhiata a what's new in 3.x sarà utile, ma quali sono alcune cose che possiamo fare in questo momento per rendere la conversione imminente più indolore (così come rendere più semplice l'output degli aggiornamenti alle versioni concorrenti se necessario)? Sto pensando in modo specifico alle linee con cui possiamo avviare i nostri script, in modo che le versioni precedenti di Python siano più simili a 3.x, sebbene altre abitudini siano benvenute.

Il codice più ovvia da aggiungere alla parte superiore dello script che mi viene in mente è:

from __future__ import division 
from __future__ import print_function 
try: 
    range = xrange 
except NameError: 
    pass 

L'abitudine cosa più ovvia che posso pensare è "{0} {1}!".format("Hello", "World") per la formattazione delle stringhe.

Eventuali altre linee e buone abitudini per entrare?

risposta

12

Il problema più grande che non può essere adeguatamente affrontato dalle modifiche a livello micro e 2to3 è il cambio di il tipo di stringa predefinito da byte a Unicode.

Se il codice deve fare qualsiasi cosa con codifiche e byte I/O, sarà necessario un po 'di sforzo manuale per convertire correttamente, in modo che le cose che devono essere byte restino byte e vengano decodificate in modo appropriato a destra palcoscenico. Scoprirai che alcuni metodi di stringa (in particolare format()) e le chiamate alle librerie richiedono stringhe Unicode, quindi potresti aver bisogno di cicli di decodifica/codifica aggiuntivi solo per usare le stringhe come Unicode anche se sono davvero solo byte.

Questo non è facilitato dal fatto che alcuni dei moduli di libreria standard Python sono stati convertiti in modo rozzo usando 2to3 senza la dovuta attenzione ai problemi di byte/codifica/codifica e quindi essi stessi commettono errori su quale tipo di stringa sia appropriato. Alcuni di questi elementi vengono messi a dura prova, ma almeno da Python 3.0 a 3.2 si dovrà affrontare un comportamento confuso e potenzialmente buggato da pacchetti come urllib, email e wsgiref che devono conoscere le codifiche dei byte.

È possibile migliorare il problema prestando attenzione ogni volta che si scrive una stringa letterale. Utilizza le stringhe u'' per qualsiasi cosa che sia intrinsecamente basata sui caratteri, le stringhe b'' per tutto ciò che è realmente byte e '' per il tipo 'stringa predefinita' dove non è importante o è necessario che corrisponda ai requisiti di utilizzo delle stringhe di una chiamata di libreria.

Purtroppo la sintassi b'' è stato introdotto solo in Python 2.6, in modo da fare questo taglia fuori gli utenti di versioni precedenti.

eta:

qual è la differenza?

Oh mio. Bene...

Un byte contiene un valore compreso tra 0 e 255 e può rappresentare un carico di dati binari (ad esempio il contenuto di un'immagine) o del testo, nel qual caso deve essere scelto uno standard per mappare un set di caratteri in quei byte. La maggior parte di questi standard di "codifica" mappa il normale set di caratteri ASCII nei byte 0-127 nello stesso modo, quindi è generalmente sicuro utilizzare le stringhe di byte per l'elaborazione di testo solo ASCII in Python 2.

Se si vuoi usare uno qualsiasi dei caratteri al di fuori del set ASCII in una stringa di byte, sei nei guai, perché ogni codifica mappa un diverso set di caratteri nei restanti valori di byte 128-255, e la maggior parte delle codifiche non può mappare ogni possibile carattere a byte. Questa è la fonte di tutti quei problemi in cui si carica un file da una locale in un'app di Windows in un'altra locale e tutte le lettere accentate o non latine cambiano a quelle sbagliate, creando un pasticcio illeggibile. (aka 'mojibake'.)

Esistono anche codifiche "multibyte", che cercano di inserire più caratteri nello spazio disponibile utilizzando più di un byte per memorizzare ciascun carattere. Questi sono stati introdotti per i locali dell'Asia orientale, poiché ci sono così tanti caratteri cinesi. Ma c'è anche UTF-8, una codifica multibyte moderna dal design migliore che può ospitare ogni carattere.

Se si sta lavorando su stringhe di byte in una codifica multibyte, e oggi probabilmente lo sarà, perché UTF-8 è molto utilizzato; in realtà, nessun'altra codifica dovrebbe essere utilizzata in un'applicazione moderna, quindi hai ancora più problemi che tenere traccia di quale codifica stai giocando. len() ti dirà la lunghezza in byte, non la lunghezza in caratteri, e se inizi a indicizzare e alterando i byte, è molto probabile che si interrompa una sequenza multibyte in due, generando una sequenza non valida e generalmente confondendo tutto.

Per questo motivo, Python 1.6 e versioni successive hanno stringhe Unicode native (digitate u'something'), dove ogni unità nella stringa è un carattere, non un byte. È possibile aggiungere len(), tagliarli, sostituirli, reindirizzarli e si comporteranno sempre in modo appropriato. Per le attività di elaborazione del testo sono indubbiamente migliori, motivo per cui Python 3 le rende il tipo di stringa predefinito (senza dover inserire un valore u prima dello '').

Il problema è che molte interfacce esistenti, come i nomi di file su sistemi operativi diversi da Windows o HTTP o SMTP, sono principalmente basate su byte, con un modo separato di specificare la codifica. Quindi, quando hai a che fare con componenti che richiedono byte, devi fare attenzione a codificare correttamente le stringhe Unicode in byte, e in Python 3 dovrai farlo esplicitamente in alcuni punti in cui prima non ne avevi bisogno.

È un dettaglio di implementazione interna che le stringhe Unicode accettano internamente "due byte" di memoria per unità. Non hai mai visto quella memoria; non dovresti pensarci in termini di byte. Le unità su cui stai lavorando sono concettualmente personaggi, indipendentemente da come Python sceglie di rappresentarli in memoria.

... a parte:

Questo non è proprio vero. Nelle "build ristrette" di Python come la build di Windows, ogni unità di una stringa Unicode non è tecnicamente un personaggio, ma una "unità di codice" UTF-16. Per i personaggi del piano multilingue di base, da 0x0000-0xFFFF non noterai alcuna differenza, ma se utilizzi personaggi al di fuori di questo intervallo di 16 bit, quelli nei 'piani astrali', scoprirai che prendono due unità invece di una, e, di nuovo, si rischia di dividere un personaggio quando lo si affetta.

Questo è piuttosto brutto, ed è successo perché Windows (e altri, come Java) si sono basati su UTF-16 come meccanismo di archiviazione in memoria prima che Unicode superasse il limite di 65.000 caratteri.Tuttavia, l'uso di questi caratteri estesi è ancora piuttosto rara, e chiunque su Windows sarà utilizzato per romperle in molte applicazioni, quindi non è probabile fondamentale per voi.

Su 'wide build', le stringhe Unicode sono costituite da unità 'code point' di caratteri reali, quindi anche i caratteri estesi al di fuori del BMP possono essere gestiti in modo coerente e semplice. Il prezzo da pagare per questo è l'efficienza: ogni unità di stringa occupa quattro byte di memoria in memoria.

+0

quando si tratta di byte e stringhe Unicode, qual è la differenza? Solo che le stringhe di byte usano un byte per carattere mentre unicode usa due byte? –

+0

"" "UTF-8 ... nessun altro codifica deve essere usato in una moderna applicazione" "" a meno che il governo mandati qualcos'altro, per esempio 'Gb18030' :-) –

+0

Questo è stato un ottimo * * spiegazione circa la differenza tra le stringhe di byte e Unicode. Ero più o meno familiarità con ASCII v. Unicode, ma (ovviamente) non ero a conoscenza di come Python (esp 3.x) affrontato con loro. Vorrei poter up-voto più di una volta;) –

5

Sto provando a prendere l'abitudine di usare cose come var1//var2 ogni volta che effettivamente voglio dividere interi (e non un float). Non un grosso passo verso Python 3, ma almeno non dovrò tornare indietro e controllare tutta la mia divisione :)

Problemi correlati