Python 字符串插值

13 投票
3 回答
15369 浏览
提问于 2025-04-15 19:35

是什么原因导致了以下的行为呢?

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

但是:

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

更多信息:

  • 我的 msg 对象是从 unicode 继承来的。
  • 方法 __str__/__unicode__/__repr__ 被重写,返回的字符串是 'my message'
  • 这个 msg 对象是用字符串 'another message' 初始化的。
  • 这是在 Python 2.5 上运行的。
  • 在测试之间,变量 msg 没有被改变。
  • 这实际上是一个真实的 doctest,确实给出了这些结果。

我希望能找到一个解决方案,能够匹配这个 doctest,尽量简单(特别是在实际继承方面):

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

感谢大家的建议。

我觉得这可能不会有太大帮助,但对于好奇的读者(和勇敢的 Python 爱好者),这里是对象的实现:

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)

这是我们创建 msg 对象的方式:

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

Zope 包的版本和使用的代码如下:

编辑信息:

  • 添加/更新了被重写的方法名称
  • 添加了一些更多的信息(Python 版本和一些小信息)
  • 更新了一些错误的信息(msg 的类是基于 unicode 类,而不是 basestring
  • 添加了实际使用的类的实现

3 个回答

3

我觉得你的问题在于你试图扩展一个内置的东西。魔法方法 __ 是不会被内置对象调用的。你可能需要做一些包装和委托的操作,像这样(这个代码没测试过,可能Anurag已经先做了):

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) 

更新 1 - 看起来 __ 方法确实会被内置类的子类调用。

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

所以这里肯定有一些不一致的地方……

6

所以问题是,下面这样的类表现得很奇怪

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

这段代码输出

my message
my message
another message

我不太确定为什么会这样,也不知道怎么修复,不过我试着用一种很粗糙的方法把 Msg 包裹起来,但不确定这对提问者的问题有没有帮助

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

输出结果:

my message
my message
my message
8

更新 2:请在横线下方找到原始回答,其中包括一个简单的类示例,展示了提问者所描述的行为。关于我在研究Python源代码(版本2.6.4)时所能推测的内容:

文件 Include/unicodeobject.h 中包含以下两行(在我这个(稍微有点旧的)版本中是第436-437行):

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

这段代码在格式化代码中到处使用,按照我的理解,这意味着在字符串格式化时,任何继承自 unicode 的对象都会被直接访问,以便可以直接使用它的unicode字符串缓冲区,而不需要调用任何Python方法。我相信这在性能方面是好的(而且与Juergen在这个回答中的评论猜测非常一致)。

对于提问者的问题,这可能意味着要让事情按照提问者希望的方式工作,可能只有在接受Anurag Uniyal的包装类想法的情况下才有可能。如果不接受,那么我现在想到的唯一办法就是在将这些对象插入字符串时,把它们包装在 str / unicode 中……唉。(我真心希望我只是错过了一个更简洁的解决方案,希望有人能在一会儿指出来!)


(更新:这条信息是在提问者包含他类的代码前大约一分钟发布的,但我还是把它留在这里(1)为了下面代码的猜测/初步解释,(2) 为了提供一个简单的示例,展示如何产生这种行为(Anurag Uniyal后来提供了另一个示例,直接调用 unicode 的构造函数,而不是通过 super),(3) 希望以后能编辑一些内容,帮助提问者获得想要的行为。)

这是一个实际工作方式与提问者描述相符的类示例(Python 2.6.4,确实会产生一个弃用警告 -- /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'

在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

显然,字符串插值将这个对象视为 unicode 的实例(直接调用 unicode__str__ 实现),而其他函数则将其视为 Foo 的实例。至于这在内部是如何发生的,为什么会这样工作,以及这是否是一个bug或特性,我真的不知道。

至于如何修复提问者的对象……好吧,如果不看到它的代码,我怎么知道呢???给我代码,我保证会考虑一下! 好吧,我在考虑……到目前为止没有想法。

撰写回答