2009-05-01 13 views
95

Już otrzymaliśmy bazę kodu działającą w Pythonie 2.6. W celu przygotowania do Python 3.0, rozpoczęliśmy dodając:Jakieś propozycje używając unicode_literals w Pythonie 2.6?

 
from __future__ import unicode_literals 

do naszych .py plików (jak je zmodyfikować). Zastanawiam się, czy ktokolwiek inny to robił i natknął się na jakieś nieoczywiste pouczenia (być może po spędzeniu dużej ilości czasu na debugowaniu).

Odpowiedz

99

Głównym źródłem problemów miałem Praca z ciągami znaków Unicode jest kiedy wymieszać kodowanie UTF-8 ciągi Unicode One.

Weźmy na przykład następujące skrypty.

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 

Wyjście działa python one.py jest:

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) 

W tym przykładzie two.name jest łańcuchem UTF-8 (nie unicode), ponieważ nie importował unicode_literals i one.name to ciąg znaków Unicode. Kiedy miksujesz oba, python próbuje dekodować zakodowany ciąg (zakładając, że jest to ASCII) i przekonwertować go do Unicode i nie powiedzie się. To zadziałałoby, gdybyś zrobił print name + two.name.decode('utf-8').

To samo może się zdarzyć, jeśli zakodujesz łańcuch i spróbuj go później połączyć. Na przykład to działa:

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

wyjściowa:

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

Ale po dodaniu import unicode_literals to nie:

# 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 

wyjściowy:

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) 

Nie powiedzie się, ponieważ 'DEBUG: %s' jest ciągiem znaków Unicode i dlatego python próbuje zdekodować html. Kilka sposobów naprawienia drukowania: albo robisz print str('DEBUG: %s') % html lub print 'DEBUG: %s' % html.decode('utf-8').

Mam nadzieję, że pomoże to zrozumieć potencjalne błędy podczas używania ciągów Unicode.

+11

Proponuję pójść z rozwiązaniami 'decode()' zamiast rozwiązań 'str()' lub 'encode()': im częściej używasz obiektów Unicode, tym wyraźniejszy jest kod, ponieważ to, czego chcesz, to manipulować ciągami znaków, a nie tablicami z zewnętrznie domniemanym kodowaniem. – EOL

+7

Napraw swoją terminologię.'gdy miksowane są ciągi zakodowane za pomocą UTF-8 z UTF-8 w formacie Unicode' oraz Unicode i nie mają 2 różnych kodowań; Unicode jest standardem, a UTF-8 jest jednym z definiowanych przez niego kodowań. – Kos

+10

@Kos: Myślę, że on oznacza mieszanie "zakodowanych w UTF-8 łańcuchów" * obiektów * z unikodami (stąd dekodowane) * obiektami *. Pierwszy jest typu 'str', drugi to typ' unicode'. Będąc różnymi obiektami, problem może powstać, jeśli spróbujesz je podsumować/połączyć/interpolować – MestreLion

13

znalazłem, że jeśli dodać dyrektywę unicode_literals należy również dodać coś takiego:

# -*- coding: utf-8 

do pierwszej lub drugiej linii pliku .py. W przeciwnym razie linie takie jak:

foo = "barré" 

spowodować błąd, takie jak:

 
SyntaxError: Non-ASCII character '\xc3' in file mumble.py on line 198, 
but no encoding declared; see http://www.python.org/peps/pep-0263.html 
for details 
+5

+1. Ale czy nie powinieneś tego robić w warunkach * wszystkich *? –

+5

@IanMackinnon: Python 3 zakłada, że ​​pliki mają UTF8 domyślnie – endolith

+3

@endolith: Ale Python 2 nie, i spowoduje błąd składni, jeśli użyjesz znaków spoza ASCII * nawet w komentarzach *! Tak więc IMHO '# - * - coding: utf-8' jest praktycznie obowiązującą instrukcją niezależnie od tego, czy używasz' unicode_literals' czy nie – MestreLion

16

Również w wersji 2.6 (przed pythonem 2.6.5 RC1 literały +) Unicode nie grać ładny z argumentów słowa kluczowego (issue4978):

Poniższy kod na przykład działa bez unicode_literals, ale nie z TypeError: keywords must be string jeśli jest stosowany 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 
+16

Po prostu FYI, python 2.6.5 RC1 + naprawił to. –

8

Należy także wziąć pod uwagę to, że unicode_literal wpływa eval() ale nie repr() (zachowanie niesymetryczne, które imho jest błąd), to eval(repr(b'\xa4')) nie będzie równa b'\xa4' (jak to będzie Pythona 3).

Idealnie, po kod powinien być niezmienne, które zawsze powinny działać dla wszystkich kombinacji unicode_literals Python {2.7} 3.x użytkowania:

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+ 

Drugi twierdzenie stanie pracować, ponieważ repr('\xa4') ocenia się na u'\xa4' w Pythonie 2.7.

+1

Czuję, że większy problem polega na tym, że używasz 'repr' do regeneracji obiektu. Dokumentacja '' repr' (https://docs.python.org/2/library/functions.html#func-repr) wyraźnie stwierdza, że ​​nie jest to * wymóg. Moim zdaniem, to releguje 'repr' do czegoś użytecznego tylko do debugowania. – jpmc26

5

Jest ich więcej.

Istnieją biblioteki i wbudowane, które oczekują ciągów, które nie tolerują Unicode.

Dwa przykłady:

wbudowane:

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

(nieznacznie esotic) nie działa z unicode_literals: typ() oczekuje ciąg.

biblioteka:

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

nie działa: biblioteka wx PubSub spodziewa typ wiadomości ciąg.

Były to ezoteryczny i łatwo ustalony z

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

ale ten ostatni jest katastrofalne, jeśli kod jest pełna wywołań pub.sendMessage() (które kopalnia).

Dang it, eh?!?

+3

A rzeczy typu również przeciekają do metaclasses - tak w Django wszelkie łańcuchy, które deklarujesz w 'class Meta:' powinny być 'b'field_name''' –

+2

Tak ... w moim przypadku zdałem sobie sprawę, że warto było szukać i zamień wszystkie ciągi sendMessage na wersje b '. Jeśli chcesz uniknąć przerażającego wyjątku "dekodowania", nie ma to jak ścisłe używanie unicodu w twoim programie, konwertowanie na wejście i wyjście w razie potrzeby ("kanapka unicode", o której mowa w jakimś artykule, który przeczytałem na ten temat). Ogólnie rzecz biorąc, unicode_literals to dla mnie duża wygrana ... – GreenAsJade