2010-08-24 14 views
6

Sono alle prese con la conversione di stampa e unicode. Ecco un codice eseguito nell'interprete di Windows 2.5.Stampa di oggetti e unicode, cosa c'è sotto il cofano? Quali sono le buone linee guida?

>>> import sys 
>>> print sys.stdout.encoding 
cp850 
>>> print u"é" 
é 
>>> print u"é".encode("cp850") 
é 
>>> print u"é".encode("utf8") 
├® 
>>> print u"é".__repr__() 
u'\xe9' 

>>> class A(): 
... def __unicode__(self): 
...  return u"é" 
... 
>>> print A() 
<__main__.A instance at 0x0000000002AEEA88> 

>>> class B(): 
... def __repr__(self): 
...  return u"é".encode("cp850") 
... 
>>> print B() 
é 

>>> class C(): 
... def __repr__(self): 
...  return u"é".encode("utf8") 
... 
>>> print C() 
├® 

>>> class D(): 
... def __str__(self): 
...  return u"é" 
... 
>>> print D() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128) 

>>> class E(): 
... def __repr__(self): 
...  return u"é" 
... 
>>> print E() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128) 

Così, quando una stringa unicode viene stampato, non è che sia __repr__() funzione che si chiama e stampata.
Tuttavia, quando viene stampato un oggetto, viene chiamato __str__() o __repr__() (se __str__ non è implementato), non __unicode__(). Entrambi non possono restituire una stringa Unicode.
Ma perché? Perché se __repr__() o __str__() restituisce una stringa unicode, non dovrebbe essere lo stesso comportamento di quando stampiamo una stringa Unicode? Ho altre parole: perché print D() è diverso da print D().__str__()

Mi manca qualcosa?

Questi esempi mostrano anche che se si desidera stampare un oggetto rappresentato con stringhe Unicode, è necessario codificarlo su una stringa di oggetto (tipo str). Ma per una buona stampa (evita "├®"), dipende dalla codifica sys.stdout.
Quindi, devo aggiungere u"é".encode(sys.stdout.encoding) per ciascuno dei miei metodi __str__ o __repr__? Oppure restituisci repr (u "é")? Cosa succede se utilizzo le tubazioni? La codifica è la stessa di sys.stdout?

Il mio problema principale è rendere una classe "stampabile", ovvero print A() stampa qualcosa completamente leggibile (non con i caratteri unicode di \ x ***). Qui è il comportamento/code male che deve essere modificato:

class User(object): 
    name = u"Luiz Inácio Lula da Silva" 
    def __repr__(self): 
     # returns unicode 
     return "<User: %s>" % self.name 
     # won't display gracefully 
     # expl: print repr(u'é') -> u'\xe9' 
     return repr("<User: %s>" % self.name) 
     # won't display gracefully 
     # expl: print u"é".encode("utf8") -> print '\xc3\xa9' -> ├® 
     return ("<User: %s>" % self.name).encode("utf8") 

Grazie!

risposta

8

Python non ha molti vincoli di tipo semantico su determinate funzioni e metodi, ma ha pochi, ed ecco uno di loro: __str__ (in Python 2. *) deve restituire una stringa di byte. Come al solito, se viene trovato un oggetto unicode dove è richiesta una stringa di byte, la codifica predefinita corrente (solitamente 'ascii') viene applicata nel tentativo di rendere la stringa di byte richiesta dall'oggetto Unicode in questione.

Per questa operazione, la codifica (se presente) di un dato oggetto file è irrilevante, poiché ciò che viene restituito da __str__ potrebbe essere in procinto di essere stampato, o potrebbe essere soggetto a trattamento completamente diverso e non correlato. Il tuo scopo nella chiamata __str__ non ha importanza per la chiamata stessa e i suoi risultati; Python, in generale, non prende in considerazione il "contesto futuro" di un'operazione (cosa farai con il risultato dopo che l'operazione è stata eseguita) nel determinare la semantica dell'operazione.

Questo perché Python non sempre conosce le sue intenzioni future, e tenta di ridurre al minimo la quantità di sorpresa.print str(x) e s = str(x); print s (le stesse operazioni eseguite in un sorso contro due), in particolare, devono avere gli stessi effetti; se il secondo caso, ci sarà un'eccezione se str(x) non può produrre validamente una stringa di byte (cioè, ad esempio, x.__str__() non può), e quindi l'eccezione dovrebbe verificarsi anche nell'altro caso.

print stessa (dal 2,4 Credo), quando presentato con un oggetto unicode, prende in considerazione l'attributo .encoding (se presente) del flusso bersaglio (per default sys.stdout); altre operazioni, non ancora connesse a un determinato flusso di destinazione, non - e str(x) (ovvero x.__str__()) è solo un'operazione del genere.

Spero che questo ha aiutato a mostrare il motivo per il comportamento che vi infastidisce ...

Edit: il PO ora chiarisce "Il mio problema principale è quello di fare una classe 'stampabile', vale a dire di stampa A() stampa qualcosa completamente leggibile (non con i caratteri unicode di \ x ***). ". Ecco l'approccio penso funziona meglio per questo obiettivo specifico:

import sys 

DEFAULT_ENCODING = 'UTF-8' # or whatever you like best 

class sic(object): 

    def __unicode__(self): # the "real thing" 
     return u'Pel\xe9' 

    def __str__(self):  # tries to "look nice" 
     return unicode(self).encode(sys.stdout.encoding or DEFAULT_ENCODING, 
            'replace') 

    def __repr__(self):  # must be unambiguous 
     return repr(unicode(self)) 

Cioè, questo approccio si concentra sulla __unicode__ come il modo principale per le istanze della classe per formattare se stessi - ma dal momento che (in Python 2) print chiama __str__ invece, ha delegato a __unicode__ con il meglio che può fare in termini di codifica. Non perfetto, ma la dichiarazione print di Python 2 è tutt'altro che perfetta ;-).

__repr__, da parte sua, deve sforzarsi di essere inequivocabili, cioè, non a "un aspetto gradevole" a scapito di rischiare ambiguità (idealmente, quando possibile, dovrebbe restituire una stringa di byte che, se passato a eval, renderebbe un'istanza uguale a quello attuale ... che è lontano da sempre fattibile, ma la mancanza di ambiguità è il centro assoluto della distinzione tra __str__ e __repr__, e fortemente consiglia rispettando tale distinzione !).

+0

Grazie Alex, ora vedo perché 'print D()' ha un comportamento diverso da 'print D() .__ str __()'. E 'stato un po' di confusione. Quindi, potresti condividere le linee guida quando devi gestire stringhe unicode nei metodi __repr__ o __str__? Dovrei restituire un repr() dell'intero unicode o codificarlo su un oggetto stringa? Oppure potrei ancora restituire un unicode e impostare la codifica con sys.setdefaultencoding in un modulo del sito personalizzato (ma ho trovato che questo è troppo invadente). – Thorfin

+0

@Thorfin, per restituire Unicode, implementare '__unicode__'. '__str__' dovrebbe sempre restituire una stringa di byte, e' __repr__' una stringa di byte che "idealmente" (ma non è sempre possibile o ragionevole) si potrebbe 'eval' costruire un nuovo oggetto. –

+0

Credo che "__unicode__" sia chiamato solo in congiunzione con unicode(), e sfortunatamente questo non risolve i miei problemi. Ho aggiunto alcune informazioni alla fine del corpo della mia domanda iniziale. Grazie ancora. – Thorfin

0

Suppongo che il tuo sys.getdefaultencoding() sia ancora 'ascii'. E penso che questo sia usato ogni volta che vengono applicati str() o repr() di un oggetto. Puoi cambiarlo con sys.setdefaultencoding(). Non appena si scrive su uno stream, però, sia esso STDOUT o un file, è necessario rispettare la sua codifica. Ciò si applicherebbe anche alle tubazioni sul guscio, IMO. Suppongo che "print" onori la codifica STDOUT, ma l'eccezione si verifica prima del "print" viene invocato quando costruisce il suo argomento.

Problemi correlati