Qualche problema con unicode_literals in Python 2.6?


101

Abbiamo già ottenuto la nostra base di codice in esecuzione con Python 2.6. Per prepararci a Python 3.0, abbiamo iniziato ad aggiungere:

from __future__ import unicode_literals

nei nostri .pyfile (man mano che li modifichiamo). Mi chiedo se qualcun altro lo abbia fatto e si sia imbattuto in qualche trucco non ovvio (forse dopo aver passato molto tempo a eseguire il debug).

Risposte:


101

La principale fonte di problemi che ho avuto a lavorare con le stringhe unicode è quando mescoli stringhe codificate utf-8 con stringhe unicode.

Ad esempio, considera i seguenti script.

two.py

# encoding: utf-8
name = 'helló wörld from two'

one.py

# encoding: utf-8
from __future__ import unicode_literals
import two
name = 'helló wörld from one'
print name + two.name

L'output della corsa python one.pyè:

Traceback (most recent call last):
  File "one.py", line 5, in <module>
    print name + two.name
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

In questo esempio, two.nameè una stringa codificata utf-8 (non Unicode) poiché non è stata importata unicode_literalsed one.nameè una stringa Unicode. Quando mescoli entrambi, python prova a decodificare la stringa codificata (supponendo che sia ascii) e la converte in unicode e fallisce. Funzionerebbe se lo facessi print name + two.name.decode('utf-8').

La stessa cosa può accadere se codifichi una stringa e provi a mescolarli in un secondo momento. Ad esempio, questo funziona:

# encoding: utf-8
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
    html = html.encode('utf-8')
print 'DEBUG: %s' % html

Produzione:

DEBUG: <html><body>helló wörld</body></html>

Ma dopo aver aggiunto import unicode_literalsNON:

# encoding: utf-8
from __future__ import unicode_literals
html = '<html><body>helló wörld</body></html>'
if isinstance(html, unicode):
    html = html.encode('utf-8')
print 'DEBUG: %s' % html

Produzione:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print 'DEBUG: %s' % html
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 16: ordinal not in range(128)

Non riesce perché 'DEBUG: %s'è una stringa Unicode e quindi Python cerca di decodificare html. Un paio di modi per correggere la stampa sono facendo print str('DEBUG: %s') % htmlo print 'DEBUG: %s' % html.decode('utf-8').

Spero che questo ti aiuti a capire i potenziali trucchi quando usi le stringhe Unicode.


11
Suggerirei di andare con le decode()soluzioni invece delle soluzioni str()o encode(): più spesso usi oggetti Unicode, più chiaro è il codice, poiché ciò che vuoi è manipolare stringhe di caratteri, non matrici di byte con una codifica implicita esternamente.
Eric O Lebigot

8
Correggi la tua terminologia. when you mix utf-8 encoded strings with unicode onesUTF-8 e Unicode non sono 2 codifiche diverse; Unicode è uno standard e UTF-8 è una delle codifiche che definisce.
Kos

11
@Kos: Credo che significa mix "utf-8 corde codificati" oggetti con unicode (quindi decodificata) oggetti . Il primo è di tipo str, il secondo è di tipo unicode. Essendo oggetti diversi, potrebbe sorgere un problema se provi a
sommarli

Questo si applica a python>=2.6o python==2.6?
joar

16

Anche in 2.6 (prima di python 2.6.5 RC1 +) i letterali unicode non funzionano bene con gli argomenti delle parole chiave ( problema4978 ):

Il codice seguente, ad esempio, funziona senza unicode_literals, ma fallisce con TypeError: keywords must be stringse viene utilizzato unicode_literals.

  >>> def foo(a=None): pass
  ...
  >>> foo(**{'a':1})
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
      TypeError: foo() keywords must be strings

17
Solo per tua informazione, python 2.6.5 RC1 + ha risolto questo problema.
Mahmoud Abdelkader

13

Ho scoperto che se aggiungi la unicode_literalsdirettiva dovresti anche aggiungere qualcosa come:

 # -*- coding: utf-8

alla prima o alla seconda riga del file .py. Altrimenti linee come:

 foo = "barré"

si traduce in un errore come:

SyntaxError: carattere non ASCII '\ xc3' nel file mumble.py alla riga 198,
 ma nessuna codifica dichiarata; vedere http://www.python.org/peps/pep-0263.html
 per dettagli

5
@IanMackinnon: Python 3 presume che i file siano UTF8 per impostazione predefinita
endolith

3
@endolith: ma Python 2 non lo fa e darà l'errore di sintassi se usi caratteri non ASCII anche nei commenti ! Quindi IMHO # -*- coding: utf-8è una dichiarazione praticamente obbligatoria indipendentemente dal fatto che tu usi unicode_literalso meno
MestreLion

Il -*-non è richiesto; se stavi andando per il modo compatibile con emacs, penso che avresti bisogno -*- encoding: utf-8 -*-(vedi anche -*-alla fine). Tutto ciò di cui hai bisogno è coding: utf-8(o anche al =posto di : ).
Chris Morgan

2
Ricevi questo errore indipendentemente dal fatto che tu from __future__ import unicode_literals.
Flimm

3
La compatibilità di Emacs richiede la # -*- coding: utf-8 -*- "codifica" (non la "codifica" o la "codifica dei file" o qualsiasi altra cosa - Python cerca semplicemente la "codifica" indipendentemente da qualsiasi prefisso).
Alex Dupuy

7

Tieni anche conto che unicode_literalinfluenzerà eval()ma non repr()(un comportamento asimmetrico che imho è un bug), cioè eval(repr(b'\xa4'))non sarà uguale a b'\xa4'(come sarebbe con Python 3).

Idealmente, il codice seguente sarebbe un invariante, che dovrebbe sempre funzionare, per tutte le combinazioni di unicode_literalse utilizzo di Python {2.7, 3.x}:

from __future__ import unicode_literals

bstr = b'\xa4'
assert eval(repr(bstr)) == bstr # fails in Python 2.7, holds in 3.1+

ustr = '\xa4'
assert eval(repr(ustr)) == ustr # holds in Python 2.7 and 3.1+

La seconda asserzione sembra funzionare, poiché repr('\xa4')restituisce u'\xa4'in Python 2.7.


2
Mi sembra che il problema più grande qui sia che stai usando reprper rigenerare un oggetto. La reprdocumentazione afferma chiaramente che questo non è un requisito. A mio parere, questo si relega repra qualcosa di utile solo per il debug.
jpmc26

5

Ci sono più.

Esistono librerie e incorporati che si aspettano stringhe che non tollerano l'unicode.

Due esempi:

incorporato:

myenum = type('Enum', (), enum)

(leggermente esotico) non funziona con unicode_literals: type () si aspetta una stringa.

biblioteca:

from wx.lib.pubsub import pub
pub.sendMessage("LOG MESSAGE", msg="no go for unicode literals")

non funziona: la libreria wx pubsub prevede un tipo di messaggio stringa.

Il primo è esoterico e facilmente risolvibile

myenum = type(b'Enum', (), enum)

ma quest'ultimo è devastante se il tuo codice è pieno di chiamate a pub.sendMessage () (che è il mio).

Dannazione, eh?!?


3
E il tipo di cose trapela anche nelle metaclassi - quindi in Django tutte le stringhe in cui dichiari class Meta:dovrebbe essereb'field_name'
Hamish Downer,

2
Sì ... nel mio caso ho capito che valeva la pena cercare e sostituire tutte le stringhe sendMessage con versioni b '. Se vuoi evitare la temuta eccezione di "decodifica", non c'è niente come usare rigorosamente unicode nel tuo programma, convertendo in input e output se necessario (il "sandwich unicode" a cui si fa riferimento in qualche articolo che ho letto sull'argomento). Nel complesso, unicode_literals è stata una grande vittoria per me ...
GreenAsJade

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.