Python 字符串插值
是什么原因导致了以下的行为呢?
>>> 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 包的版本和使用的代码如下:
- zope.i18n-3.4.0 (interpolation() 方法代码)
- zope.i18nmessageid-3.4.3 (Message 类代码)
编辑信息:
- 添加/更新了被重写的方法名称
- 添加了一些更多的信息(Python 版本和一些小信息)
- 更新了一些错误的信息(
msg
的类是基于unicode
类,而不是basestring
) - 添加了实际使用的类的实现
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
'***'
所以这里肯定有一些不一致的地方……
所以问题是,下面这样的类表现得很奇怪
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
更新 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或特性,我真的不知道。
至于如何修复提问者的对象……好吧,如果不看到它的代码,我怎么知道呢???给我代码,我保证会考虑一下! 好吧,我在考虑……到目前为止没有想法。