2010-02-22 10 views
13

¿Qué podría generar el siguiente comportamiento?cadena de python interpolación

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

Pero:

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

Más información:

  • mi objetivo msg se hereda de unicode.
  • los métodos __str__/__unicode__/__repr__ se anularon los métodos para devolver la cadena 'my message'.
  • el objeto msg se inicializó con la cadena 'another message'.
  • esto se está ejecutando en Python 2.5
  • la variable msg no fue cambiado entre las pruebas
  • Esto es realmente verdadero doctest que está realmente dando a estos resultados.

me gustaría una solución que coincida con este doctest, con un mínimo esfuerzo (especialmente alrededor de la herencia real):

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

Gracias por todas las sugerencias.

no siento que esto ayudará más, pero para los lectores curiosos (y pythonist aventurero), aquí está la implementación del objeto:

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) 

Esto es como creamos msg objeto:

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

Zope paquetes de la versión y el código utilizado son:

EDITAR INFORMACIÓN:

  • añadido/modificado los nombres de los métodos que se han anulado
  • añade algo más de información (versión pitón, e información menor)
  • actualizó alguna información incorrecta (la clase de `msg` se basa en la clase` unicode` y no `basetring`)
  • agregó la implementación real ion de la clase utilizada
+3

@extraneon: es pitón 2 .x: 'print' es una declaración, basetring, unicode! – SilentGhost

+1

cambiando el valor de la variable 'msg' entre' print's lo explicaría – van

+1

¿Tiene el código real para ese objeto? (O más bien, su clase.) Sería útil si pudieras pegarlo aquí ... –

Respuesta

8

Actualización 2: Por favor, encontrar la respuesta original, incluyendo un simple ejemplo de una clase que presenta el comportamiento descrito por el OP, debajo de la barra horizontal. En cuanto a lo que pude suponer en el curso de mi investigación sobre las fuentes de Python (v 2.6.4):

El archivo Include/unicodeobject.h contiene lo siguiente en las líneas (nos.436-7 en mi (algo antigua) de pago):

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

Esto se utiliza por todo el lugar en el código de formato, que, por lo que yo puedo decir, significa que durante el formateo de cadenas, cualquier objeto que hereda de unicode se alcanzará para que su búfer de cadena unicode se pueda usar directamente, sin llamar a ningún método de Python. Lo cual es bueno en lo que respecta al rendimiento, estoy seguro (y muy en línea con la conjetura de Juergen en un comentario sobre esta respuesta).

Para la pregunta del OP, esto probablemente signifique que hacer que las cosas funcionen de la manera en que el OP les gustaría solo puede ser posible si algo como la idea de la clase contenedora de Anurag Uniyal es aceptable para este caso de uso particular. Si no es así, lo único que viene a mi mente ahora es envolver objetos de esta clase en str/unicode donde sea que se interpongan en una cadena ... ugh. (Espero sinceramente acabo me falta una solución más limpia, que alguien va a señalar en un minuto!)


(Actualización: Este fue publicada alrededor de un minuto antes de la OP incluido el código de su clase, pero lo dejo aquí de todos modos (1) para la conjetura/intento inicial de una explicación debajo del código, (2) para un ejemplo simple de cómo producir este comportamiento (Anurag Uniyal ya ha proporcionado otro que llama al constructor unicode directamente, a diferencia de super), (3) con la esperanza de poder editar posteriormente algo para ayudar al OP a obtener el comportamiento deseado.)

Este es un ejemplo de una clase que en realidad funciona como lo describe el OP (Python 2.6.4, lo hace producir una advertencia de desaprobación - /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 par de interacciones en 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 

al parecer cadena de interpolación trata este objeto como una instancia de unicode (llamando directamente a la implementación de unicode__str__), mientras que las otras funciones tratan como una instancia de Foo. Cómo esto sucede internamente y por qué funciona así y si es un error o una característica, realmente no lo sé.

En cuanto a cómo solucionar el objeto del OP ... Bueno, ¿cómo podría saber sin ver su código ??? ¡Dame el código y prometo pensarlo! Ok, estoy pensando en ello ... No tengo ideas hasta el momento.

+0

Me parece a mí, ya que la impresión tiene un atajo, para acelerar las cosas, creo. Python tiene interfaces internas (relativamente rápidas) y interfaces externas (relativamente lentas). Supongo que alguien trató de evitar la sobrecarga ... – Juergen

+0

@Juergen: Incluida información sobre cómo son las fuentes en la respuesta ahora ... Parece que tienes razón. –

+0

@Michal: ¡gracias por la información! Python es bastante limpio como sistema, pero (a pesar de que lo entiendo y también he visto un poco) a veces algunos accesos directos se realizan internamente donde se puede obtener una gran ventaja de velocidad. Esto está bien en mi opinión, ya que esos accesos directos no son visibles en el 99% de todos los casos ... en el otro 1%, se debe hacer una solución como en este caso. Por supuesto, cuando se deja caer sobre uno, puede ser bastante sorprendente o incluso molesto ... – Juergen

6

Así problema es clase como a algo más adelante se comporta extrañamente

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 

este imprime

my message 
my message 
another message 

No estoy seguro de por qué sucede esto o cómo solucionarlo, pero un intento muy crudo envolviendo msg, pero no estoy seguro que le ayudará en la solución de 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 

de salida:

my message 
my message 
my message 
+0

No puedo permitirme cambiar el inheritage hacia unicode. Sin embargo, gracias por su ejemplo simplificado. – vaab

+0

@vaab: si miras la respuesta extendida que di, la adición de '__getattr__' reenviará todos los accesores que * serían * resueltos por herencia al atributo .msg contenido. Esta es una expresión muy poderosa en Python, y pone wrap-and-delegate a la par con la herencia, con menos acoplamiento. – PaulMcG

3

Creo que su problema es que está tratando de ampliar un built-in. Los métodos Magic __ no reciben llamadas para builtins.Creo que tendrá que hacer algún tipo de envoltura-and-delegado, como esto (no probado) (tal vez Anurag me ganó de mano):

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) 

Actualización 1 - parece que __ métodos hacer ser llamado para subclases de órdenes internas

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

Así que no hay duda alguna inconsistencia pasando ...

+0

¡Gran idea, pero esto no funciona! ;) Bueno, funciona para el doctest dado, pero el hecho de que esta clase ya no es una instancia de 'string' rompe otras comprobaciones de C en las bibliotecas comunes de Python que uso y necesito usar. Estaré más claro mañana. – vaab

+0

Ah, usted (o esas libs) están usando isinstance, tal vez? ¿Y ahora esta clase ya no hereda de la cadena de bases? Hmmm, esas comprobaciones de instancias no estarían haciendo la validación de los parámetros, ¿verdad? Este es un caso excelente que muestra por qué la comprobación de parámetros de instancia no siempre es la mejor idea en Python. – PaulMcG

Cuestiones relacionadas