2010-02-22 6 views
13

Ce qui pourrait générer le comportement suivant?interpolation de chaîne python

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

Mais:

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

Plus d'info:

  • mon objet msg est héritée de unicode.
  • les méthodes __str__/__unicode__/__repr__ méthodes ont été remplacées pour retourner la chaîne 'my message'.
  • l'objet msg a été initialisé avec la chaîne 'another message'.
  • cela est en cours d'exécution sur Python 2.5
  • n'a pas été modifié la variable msg entre les tests
  • c'est en fait réel doctest qui est vraiment de donner à ces résultats.

Je voudrais une solution qui correspond à cette doctest, en toute simplicité minimale (en particulier autour de l'héritage réel):

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

Merci pour toutes les suggestions.

Je ne pense pas que cela vous aidera plus, mais pour les lecteurs curieux (et pythonist aventureux), voici la mise en œuvre de l'objet:

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) 

Voici comment nous créons l'objet msg:

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

paquets Zope la version et le code utilisés sont:

INFO EDIT:

  • ajouté/mis à jour les noms des méthodes qui ont été outrepassée
  • a ajouté un peu plus d'informations (version python, et informations mineures)
  • mis à jour des informations erronées (la classe de `msg` est basée sur la classe` unicode` et non sur `basetring`)
  • a ajouté l'implémentation actuelle ion de la classe utilisée
+3

@extraneon: il est python 2 .x: 'print' est une instruction, basetring, unicode! – SilentGhost

+1

Modifier la valeur de la variable 'msg' entre' print's l'expliquerait – van

+1

Avez-vous le code réel pour cet objet? (Ou plutôt sa classe.) Il serait utile si vous pouviez le coller ici ... –

Répondre

8

Mise à jour 2: S'il vous plaît trouver la réponse originale, y compris un exemple simple d'une classe présentant le comportement décrit par l'OP, sous la barre horizontale. Quant à ce que j'ai pu deviner au cours de mon enquête sur les sources de Python (v. 2.6.4):

Le fichier Include/unicodeobject.h contient les lignes suivantes (nos.436-7 dans mon (un peu vieux) caisse):

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

C'est utilisé partout dans le code de formatage, qui, pour autant que je peux dire, signifie que pendant la mise en forme de chaîne, tout objet qui hérite à partir de unicode sera atteint afin que son tampon de chaîne unicode peut être utilisé directement, sans appeler les méthodes Python. Ce qui est bon en ce qui concerne la performance, j'en suis sûr (et tout à fait en accord avec la conjecture de Juergen dans un commentaire sur cette réponse). Pour la question de l'OP, cela signifie probablement que faire fonctionner les choses comme OP le souhaiterait n'est possible que si quelque chose comme l'idée de la classe wrapper d'Anurag Uniyal est acceptable pour ce cas d'utilisation particulier. Si ce n'est pas le cas, la seule chose qui me vient à l'esprit maintenant est d'envelopper les objets de cette classe dans str/unicode partout où ils sont interpolés en une chaîne ... ugh. (Je l'espère sincèrement je manque juste une solution plus propre que quelqu'un pointera dans une minute!)


(Mise à jour: Cela a été posté environ une minute avant l'OP inclus le code de sa classe, mais je le laisse ici de toute façon (1) pour la conjecture/tentative initiale d'explication en dessous du code, (2) pour un exemple simple de comment produire ce comportement (Anurag Uniyal en a fourni un autre appelant le constructeur de unicode) directement, par opposition à via super), (3) dans l'espoir de pouvoir éditer ultérieurement quelque chose pour aider le PO à obtenir le comportement souhaité.)

Voici un exemple d'une classe qui fonctionne réellement comme ce que l'OP décrit (Python 2.6.4, il produit un avertissement de dévalorisation - /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' 

Quelques interactions 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 

Apparemment l'interpolation de chaîne traite cet objet comme une instance de unicode (appelant directement l'implémentation unicode de __str__), alors que les autres fonctions la traitent comme une instance de Foo. Comment cela se passe en interne et pourquoi cela fonctionne comme ça et si c'est un bug ou une fonctionnalité, je ne sais vraiment pas.

En ce qui concerne la façon de réparer l'objet OP ... Eh bien, comment pourrais-je savoir sans voir son code ??? Donnez-moi le code et je promets d'y penser! Ok, j'y pense ... Aucune idée pour le moment.

+0

Il me semble, comme l'impression a fait un raccourci - pour accélérer les choses, je pense. Python possède des interfaces internes (relativement rapides) et des interfaces externes (relativement lentes). Je suppose, que quelqu'un a essayé d'éviter les frais généraux ... – Juergen

+0

@Juergen: Inclus quelques informations sur ce que les sources ressemblent dans la réponse maintenant ... Il semble certainement que vous avez raison. –

+0

@Michal: Merci pour l'info! Python est plutôt propre en tant que système, mais (autant que je le comprends et en ai aussi vu un peu), quelques raccourcis sont parfois faits en interne où un grand avantage de vitesse peut être obtenu. C'est OK à mon avis, puisque ces raccourcis ne sont pas visibles dans 99% des cas ... dans l'autre 1%, une solution de contournement doit être faite comme dans ce cas. Bien sûr, en bousculant un, il peut être plutôt surprenant ou même ennuyeux ... – Juergen

6

Alors problème est classe comme quelque chose se comporte en dessous weirdly

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 

cette affiche

my message 
my message 
another message 

Je ne sais pas pourquoi cela arrive ou comment le résoudre, mais une tentative très grossière en enveloppant Msg, mais pas sûr que cela vous aidera dans le problème de l'OP

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 

sortie:

my message 
my message 
my message 
+0

Je ne peux pas me permettre de changer l'héritage vers unicode. Cependant, merci pour votre exemple simplifié. – vaab

+0

@vaab: si vous regardez la réponse étendue que j'ai donnée, l'ajout de '__getattr__' transmettra tous les accesseurs qui auraient été résolus par héritage à l'attribut .msg contenu. C'est un idiome très puissant en Python, et met wrap-and-delegate à égalité avec l'héritage, avec moins de couplage. – PaulMcG

3

Je pense que votre problème est que vous essayez d'étendre un intégré. Les méthodes Magic __ ne sont pas appelées pour les builtins.Je pense que vous aurez à faire une sorte de synthèse et délégué, comme celui-ci (non testé) (peut-être Anurag m'a battu au poinçon):

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) 

Mise à jour 1 - il semble que __ méthodes faire obtenir appelé pour les sous-classes de builtins

>>> 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 
'***' 

donc, il y a certainement une incohérence en cours ...

+0

Super idée, mais cela ne fonctionne pas! ;) Cela fonctionne bien pour le doctest donné, mais le fait que cette classe ne soit plus une instance de 'string' brise d'autres vérifications C dans les bibliothèques communes Python que j'utilise et dont j'ai besoin. Je serai plus clair demain. – vaab

+0

Ah, vous (ou ces bibliothèques) utilisez isinstance, peut-être? Et maintenant cette classe n'hérite plus de basestring? Hmmm, ces contrôles d'instance ne seraient pas en train de faire la validation des paramètres, n'est-ce pas? C'est un excellent exemple montrant pourquoi la vérification des paramètres isinstance n'est pas toujours la meilleure idée en Python. – PaulMcG