2008-09-20 23 views
5

zacznę tworząc zmienną string z niektórych non-ASCIIUTF-8 zakodowanych danych na nim:Dlaczego unicode() używa str() na moim obiekcie tylko bez podanego kodu?

>>> text = 'á' 
>>> text 
'\xc3\xa1' 
>>> text.decode('utf-8') 
u'\xe1' 

Korzystanie unicode() na nim podnosi błędy ...

>>> unicode(text) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: 
        ordinal not in range(128) 

. .. ale jeśli znam kodowanie, mogę go użyć jako drugiego parametru:

>>> unicode(text, 'utf-8') 
u'\xe1' 
>>> unicode(text, 'utf-8') == text.decode('utf-8') 
True 

Teraz jeśli mam klasy, która zwraca ten tekst w sposobie __str__():

>>> class ReturnsEncoded(object): 
...  def __str__(self): 
...   return text 
... 
>>> r = ReturnsEncoded() 
>>> str(r) 
'\xc3\xa1' 

unicode(r) wydaje się używać str() na nim, ponieważ podnosi ten sam błąd jak unicode(text) powyżej:

>>> unicode(r) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: 
        ordinal not in range(128) 

Do tej pory wszystko jest zgodnie z planem!

Ale nikt nie oczekiwać, unicode(r, 'utf-8') nie będzie nawet próbować:

>>> unicode(r, 'utf-8') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: coercing to Unicode: need string or buffer, ReturnsEncoded found 

Dlaczego? Dlaczego to niespójne zachowanie? Czy to błąd? jest to przeznaczone? Bardzo dziwne.

Odpowiedz

7

Zachowanie wydaje się być mylące, ale intensywne. Powielam tutaj całą dokumentację Unicode z Python Built-In Functions documentation (dla wersji 2.5.2, jak to piszę):

Unicode ([object [, kodowanie [błędy]]])

Powrót wersja ciąg Unicode z obiektu przy użyciu jednej z następujących trybów:

Jeśli podano kodowanie i/lub błędy, unicode() zdekoduje obiekt , który może być 8-bitowym ciągiem znaków lub buforem znaków przy użyciu kodeku do kodowania. Parametr kodowania to ciąg podający nazwę kodowania; jeśli kodowanie nie jest znane, powstaje LookupError. Obsługa błędów odbywa się zgodnie z błędami ; określa to traktowanie znaków, które są niepoprawne w kodowaniu wejściowym. Jeśli błędy są "surowe" (domyślnie: ), generowany jest błąd ValueError w przypadku błędów, a wartość "ignoruj" powoduje ciche ignorowanie błędów, a wartość "zamień" powoduje oficjalny znak zastępczy Unicode, U + FFFD, aby zastąpić znaki wejściowe, których nie można zdekodować. Zobacz także moduł codecs.

Jeśli nie podano parametrów opcjonalnych, unikod() będzie naśladować zachowanie str() z wyjątkiem, że zwraca ciągi Unicode zamiast ciągów 8-bitowych. Mówiąc dokładniej, jeśli obiekt jest ciągiem lub podklasą Unicode , zwróci ten ciąg Unicode bez żadnego dodatkowego dekodowania zastosowanego.

W przypadku obiektów, które udostępniają metodę __unicode __(), wywoła ona tę metodę bez argumentów, aby utworzyć ciąg znaków Unicode. Dla wszystkich pozostałych obiektów, 8-bitowa wersja lub reprezentacja łańcuchowa jest żądana, a następnie konwertowana na ciąg znaków Unicode przy użyciu kodeku dla domyślnego kodowania w trybie "ścisłym".

Nowość w wersji 2.0. Zmieniono w wersji 2.2: Dodano obsługę __unicode __().

Więc, kiedy zadzwonić unicode(r, 'utf-8'), wymaga ciąg 8-bitowe lub bufor znaków jako pierwszego argumentu, więc to wymusza swój obiekt przy użyciu metody __str__() i próbuje rozszyfrować, że przy użyciu kodeka utf-8. Bez funkcji utf-8 funkcja szuka metody dla obiektu __unicode__(), a nie znajdowania jej, wywołuje metodę __str__(), zgodnie z sugestią, próbując użyć domyślnego kodeka do konwersji na kod Unicode.

4

nie zgaduje kodowania tekstu. Jeśli obiekt może drukować się jako unicode, zdefiniuj metodę __unicode__(), która zwraca ciąg znaków Unicode.


Tajemnicą jest to, że nie jest w rzeczywistości unicode(r) nazywając __str__() się. Zamiast tego szuka metody __unicode__(). Domyślna implementacja __unicode__() wywoła __str__(), a następnie spróbuje ją zdekodować, używając zestawu znaków ascii. Po przejściu kodowania, unicode() spodziewa się, że pierwszym obiektem będzie coś, co może zostać zdekodowane - to znaczy wystąpienie basestring.


zachowanie jest dziwne, ponieważ próbuje rozszyfrować jako ASCII jeśli nie zdam „UTF-8”. Ale jeśli przekażę "utf-8", to spowoduje to inny błąd ...

Dzieje się tak dlatego, że podając "utf-8", traktuje pierwszy parametr jako obiekt podobny do łańcucha do dekodowania. Bez niego traktuje parametr jako obiekt, który ma być wymuszony na unicode.

Nie rozumiem zamieszania. Jeśli wiesz, że atrybut obiektu text zawsze będzie kodowany w UTF-8, po prostu zdefiniuj __unicode__(), a następnie wszystko będzie działać poprawnie.

+0

Myślę, że może nie wyraziłem się jasno. Wiem to. Co mam na myśli, to wiedzieć, dlaczego unicode (r) ma inne zachowanie niż unicode (r, 'utf-8') ??? – nosklo

+0

Zachowanie jest dziwne, ponieważ próbuje dekodować jako ascii, jeśli nie przekazuję "utf-8". Ale jeśli przekazuję "utf-8", daje to inny błąd ... – nosklo