2010-02-22 25 views
13

Cosa potrebbe generare il seguente comportamento?interpolazione stringa di pitone

>>> print str(msg) 
my message 
>>> print unicode(msg) 
my message 

Ma:

>>> print '%s' % msg 
another message 

Maggiori informazioni:

  • mia msg oggetto ereditato da unicode.
  • i metodi __str__/__unicode__/__repr__ sono stati sovrascritti per restituire la stringa 'my message'.
  • l'oggetto msg è stato inizializzato con la stringa 'another message'.
  • questo è in esecuzione su Python 2.5
  • la variabile msg non è stato cambiato tra i test
  • questo è in realtà reale doctest che sta realmente dando questi risultati.

vorrei una soluzione che corrisponde a questo doctest, con il minimo sforzo (soprattutto intorno alla successione):

>>> print '%s' % msg 
my message 

Grazie per tutti i suggerimenti.

Non mi sento questo aiuterà più, ma per i lettori più curiosi (e pythonist avventurosi), ecco l'attuazione dell'oggetto:

class Message(zope.i18nmessageid.Message): 

    def __repr__(self): 
     return repr(zope.i18n.interpolate(self.default, self.mapping)) 

    def __str__(self): 
     return zope.i18n.interpolate(self.default, self.mapping) 

    def __unicode__(self): 
     return zope.i18n.interpolate(self.default, self.mapping) 

questo modo creiamo il msg oggetto:

>>> msg = Message('another message', 'mydomain', default='my message') 

Zope versione dei pacchetti e il codice usati sono:

EDIT INFORMAZIONI:

  • aggiunto/aggiornato i nomi dei metodi che sono stati sovrascritto
  • aggiunto qualche informazione in più (versione di Python, e informazioni minori)
  • ha aggiornato alcune informazioni errate (la classe di `msg` è basata sulla classe` unicode` e non `basestring`)
  • aggiunto l'implementazione effettiva ione della classe utilizzata
+3

@extraneon: è python 2 .x: 'print' è un'istruzione, basestring, unicode! – SilentGhost

+1

cambiando il valore della variabile 'msg' tra' print' s lo spiegheremo – van

+1

Hai il codice effettivo per quell'oggetto? (O meglio la sua classe.) Sarebbe utile se tu potessi incollarlo qui ... –

risposta

8

Aggiornamento 2: Si prega di trovare la risposta originale, compreso un semplice esempio di una classe esibendo il comportamento descritto dal PO, sotto la barra orizzontale. Per quanto mi è stato in grado di supporre nel corso della mia inchiesta sulle fonti di Python (v. 2.6.4):

Il file Include/unicodeobject.h contiene quanto segue alle linee (n.436-7 nella mia (un po 'vecchio) checkout):

#define PyUnicode_AS_UNICODE(op) \            
     (((PyUnicodeObject *)(op))->str) 

Questo è utilizzato in tutto il luogo nel codice di formattazione, che, per quanto posso dire, significa che durante la formattazione di stringhe, qualsiasi oggetto che eredita da unicode verrà raggiunto in modo che il suo buffer di stringa unicode possa essere utilizzato direttamente, senza chiamare alcun metodo Python. Che è buono per quanto riguarda le prestazioni, ne sono sicuro (e molto in linea con la congettura di Juergen in un commento su questa risposta).

Per la domanda dell'OP, questo probabilmente significa che far funzionare le cose come l'OP vorrebbe che fossero possibili solo se qualcosa come l'idea della classe wrapper di Anurag Uniyal è accettabile per questo particolare caso d'uso. Se non lo è, l'unica cosa che mi viene in mente ora è di avvolgere oggetti di questa classe in str/unicode ovunque siano interpolati in una stringa ... ugh. (Spero sinceramente mi manca solo una soluzione più pulita che qualcuno indicherà in un minuto!)


(Aggiornamento: Questo è stato inviato circa un minuto prima che il PO compreso il codice della sua classe, ma lo lascio comunque (1) per la congettura/tentativo iniziale di una spiegazione al di sotto del codice, (2) per un semplice esempio di come produrre questo comportamento (Anurag Uniyal ne ha fornito un altro chiamando il costruttore unicode direttamente, al contrario di via super), (3) nella speranza di poter modificare in seguito qualcosa per aiutare l'OP nell'ottenere il comportamento desiderato.)

Ecco un esempio di una classe che in realtà funziona come quello che il PO descrive (Python 2.6.4, che fa produrre un deprecazione avvertimento - /usr/bin/ipython:3: DeprecationWarning: object.__init__() takes no parameters):

class Foo(unicode): 
    def __init__(self, msg): 
     super(unicode, self).__init__(msg) 
    def __str__(self): return 'str msg' 
    def __repr__(self): return 'repr msg' 
    def __unicode__(self): return u'unicode msg' 

Un paio di interazioni in IPython:

In [12]: print(Foo("asdf")) 
asdf 

In [13]: str(Foo("asdf")) 
Out[13]: 'str msg' 

In [14]: print str(Foo("asdf")) 
-------> print(str(Foo("asdf"))) 
str msg 

In [15]: print(str(Foo("asdf"))) 
str msg 

In [16]: print('%s' % Foo("asdf")) 
asdf 

Apparentemente stringa di interpolazione considera questo oggetto come un esempio di unicode (chiamando direttamente l'unicode attuazione __str__), mentre le altre funzioni trattano come un esempio di Foo. In che modo ciò accade internamente e perché funziona così e se si tratta di un bug o di una funzionalità, davvero non lo so.

Per quanto riguarda come risolvere l'oggetto dell'Op ... Bene, come faccio a sapere senza vedere il suo codice ??? Dammi il codice e prometto di pensarci! Ok, ci sto pensando ... Nessuna idea finora.

+0

Mi sembra, come la stampa ha fatto qualche scorciatoia - per accelerare le cose, penserei. Python ha interfacce interne (relativamente veloci) e interfacce esterne (relativamente lente). Immagino che qualcuno abbia cercato di evitare l'overhead ... – Juergen

+0

@Juergen: alcune informazioni su come appaiono le fonti nella risposta ora ... Sembra certamente che tu abbia ragione. –

+0

@Michal: Grazie per le informazioni! Python è piuttosto pulito come un sistema, ma a volte ho capito che alcune scorciatoie sono realizzate internamente dove si può ottenere un grande vantaggio di velocità. Questo è OK secondo me, dal momento che quelle scorciatoie non sono visibili nel 99% di tutti i casi ... nell'altro 1%, una soluzione alternativa deve essere fatta come in questo caso. Ovviamente, quando si appunta su uno, può essere piuttosto sorprendente o addirittura fastidioso ... – Juergen

6

Così problema è di classe come a qualcosa di seguito si comporta stranamente

class Msg(unicode): 
    def __init__(self, s): 
     unicode.__init__(self, s) 

    __unicode__ = __repr__ = __str__ = lambda self: "my message" 

msg = Msg("another message") 
print str(msg) 
print unicode(msg) 
print "%s"%msg 

questa stampa

my message 
my message 
another message 

io non sono sicuro perché questo accade o come risolvere il problema, ma un tentativo molto grezzo avvolgendo msg, ma non sono sicuro che vi aiuterà nel problema del PO

class MsgX(object): 
    def __init__(self, s): 
     self._msg = Msg(s) 

    __unicode__ = __repr__ = __str__ = lambda self: repr(self._msg) 

msg = MsgX("another message") 
print str(msg) 
print unicode(msg) 
print "%s"%msg 

uscita:

my message 
my message 
my message 
+0

Non posso permettermi di cambiare l'eredità verso l'unicode. Tuttavia, grazie per l'esempio semplificato. – vaab

+0

@vaab: se si guarda la risposta estesa che ho dato, l'aggiunta di '__getattr__' inoltrerà tutti gli accessor che * sarebbero * stati risolti per ereditarietà con l'attributo .msg contenuto. Questo è un idioma molto potente in Python e mette wrap-and-delegate alla pari con l'ereditarietà, con un minore accoppiamento. – PaulMcG

3

Penso che il problema sia che si sta tentando di estendere un built-in. I metodi Magic __ non vengono richiamati per i builtin.Penso che si dovrà fare una sorta di avvolgente e-delega, come questo (non testato) (forse Anurag mi ha battuto al punzone):

class Message(object): 

    def __init__(self, strvalue, domain, default='my message'): 
     self.msg = zope.i18nmessageid.Message(strvalue,domain,default) 

    def __getattr__(self,attr): 
     return getattr(self.msg,attr) 

    def __repr__(self): 
     return repr(zope.i18n.interpolate(self.msg.default, self.msg.mapping)) 

    def __str__(self): 
     return zope.i18n.interpolate(self.msg.default, self.msg.mapping) 

    def __unicode__(self): 
     return zope.i18n.interpolate(self.msg.default, self.msg.mapping) 

Update 1 - sembra che __ metodi fare ottenere chiamato per sottoclassi di comandi incorporati

>>> class Z(int): 
... def __add__(self,other): return self*other 
... def __str__(self): return "***" 
... 
>>> a = Z(100) 
>>> a + 2 
200 
>>> a 
100 
>>> str(a) 
'***' 
>>> "%s" % a 
'***' 

Quindi c'è sicuramente una certa incoerenza in corso ...

+0

Ottima idea, ma questo non funziona! ;) Funziona bene per il doctest dato, ma il fatto che questa classe non sia più un'istanza di 'string' interrompe alcuni altri controlli C nelle librerie comuni Python che uso e devo usare. Sarò più chiaro domani. – vaab

+0

Ah, tu (o quelle libs) stai usando isinstance, forse? E ora questa classe non eredita più da basestring? Hmmm, quei controlli di isinstance non avrebbero fatto la validazione dei parametri, vero? Questo è un caso eccellente che mostra perché il controllo dei parametri di istanza non è sempre la migliore idea in Python. – PaulMcG

Problemi correlati