Что могло генерировать следующее поведение?
>>> 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'
.msg
не был изменен между тестамиЯ хотел бы решение, которое соответствует этому doctest с минимальной суетой (особенно вокруг фактического наследования):
>>> print '%s' % msg
my message
Спасибо за все предложения.
Я не чувствую, что это поможет больше, но для любопытных читателей (и предприимчивый pythonist), вот реализация объекта:
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 = Message('another message', 'mydomain', default='my message')
Версия пакетов Zope и используемый код:
ИНФОРМАЦИЯ О РЕДАКТИРОВАНИИ:
Обновление 2: Пожалуйста, найдите оригинальный ответ, включая простой пример класса, демонстрирующего поведение, описанное в ОП, ниже горизонтальной полосы. Что касается того, что я смог предположить в ходе изучения исходных текстов Python (v. 2.6.4):
Файл Include/unicodeobject.h
содержит следующие строки (№№. 436-7 в моем (несколько старом) чекбоксе):
#define PyUnicode_AS_UNICODE(op) \
(((PyUnicodeObject *)(op))->str)
Это используется повсюду в коде форматирования, что, насколько я могу судить, означает, что во время форматирования строк любой объект, наследующий от unicode
, будет достигнут, чтобы его буфер строк юникода мог быть использован напрямую, без вызова каких-либо методов Python. Это хорошо с точки зрения производительности, я уверен (и очень соответствует предположению Юргена в комментарии к этому ответу).
Для вопроса ОП это, вероятно, означает, что заставить все работать так, как хотелось бы ОП, возможно только в том случае, если что-то вроде идеи класса-обертки Анурага Унияла приемлемо для данного конкретного случая использования. Если это не так, то единственное, что приходит мне на ум сейчас - это обернуть объекты этого класса в str
/ unicode
везде, где они интерполируются в строку... уф. (Я искренне надеюсь, что я просто упустил более чистое решение, на которое кто-нибудь укажет через минуту! )
(Update: Это было опубликовано примерно за минуту до того, как ОП включил код своего класса, но я все равно оставляю его здесь (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
. Как это происходит внутри, почему это так работает и является ли это ошибкой или особенностью, я действительно не знаю.
Что касается того, как исправить объект ОП... Ну, откуда мне знать, не видя его кода? Дайте мне код, и я обещаю подумать об этом! Хорошо, я думаю об этом... Пока никаких идей.
Я думаю, ваша проблема в том, что вы пытаетесь расширить встроенный. Методы Magic __
не вызываются для встроенных функций. Я думаю, вам придется сделать что-то вроде обертывания и делегирования, вроде этого (непроверено) (возможно, Анураг победил меня до упора):
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
это print
my message
my message
another message
Я не уверен, почему это происходит и как это исправить, но очень грубая попытка обернуть Msg, но не уверен, что это поможет в выводе 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
:
my message
my message
my message