Интерполяция строк python
Что может вызвать следующее поведение?
>>> print str(msg)
my message
>>> print unicode(msg)
my message
Но:
>>> print '%s' % msg
another message
Дополнительная информация:
- my
msg
объект наследуется от unicode
.
- методы
__str__
/__unicode__
/__repr__
были переопределены, чтобы вернуть строку 'my message'
.
- объект
msg
был инициализирован строкой 'another message'
.
- это работает на python 2.5
- переменная
msg
не была изменена между тестами
- Это действительно настоящий докт, который действительно дает эти результаты.
Я бы хотел, чтобы решение, которое соответствует этому доктрину, с минимальной суматохой (особенно вокруг фактического наследования):
>>> print '%s' % msg
my message
Спасибо за все предложения.
Я не чувствую, что это поможет больше, но для любопытных читателей (и авантюрного питониста), здесь реализация объекта:
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`)
- добавлена фактическая реализация используемого класса
Ответы
Ответ 1
Обновление 2: найдите исходный ответ, включая простой пример класса, демонстрирующего поведение, описанное OP, ниже горизонтальной полосы. Что касается того, что я смог догадаться в ходе моего исследования источников Python (версия 2.6.4):
Файл Include/unicodeobject.h
содержит следующие строки (№ 436-7 в моей (несколько старой) кассете):
#define PyUnicode_AS_UNICODE(op) \
(((PyUnicodeObject *)(op))->str)
Это используется повсеместно в коде форматирования, который, насколько я могу судить, означает, что во время форматирования строки любой объект, который наследует от unicode
, будет достигнут так, чтобы его буфер строки в Юникоде мог быть используется напрямую, не вызывая никаких методов Python. Что хорошо по производительности, я уверен (и очень согласен с гипотезой Юргена в комментарии к этому ответу).
Для вопроса OP это, вероятно, означает, что все работает так, как бы им хотелось бы, возможно, только если что-то вроде идеи класса оболочки Anurag Uniyal приемлемо для данного конкретного случая использования. Если это не так, единственное, что приходит мне на ум, - это обернуть объекты этого класса в str
/unicode
, где бы их не было интерполировано в строку... тьфу. (Я искренне надеюсь, что мне просто не хватает чистого решения, которое кто-то укажет через минуту!)
(Обновление: это было опубликовано примерно за минуту до того, как OP включил код своего класса, но я все равно оставляю его здесь (1) для гипотезы/первоначальной попытки объяснения ниже кода, (2) для простой пример того, как произвести это поведение (Anurag Uniyal с тех пор предоставил другому, который вызывал конструктор unicode
напрямую, а не через super
), (3) в надежде, что позже сможет редактировать что-то, чтобы помочь OP в получении желаемого поведения.)
Вот пример класса, который действительно работает так, как описывает OP (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
. Как это происходит внутри, и почему это работает так и есть ли это ошибка или функция, я действительно не знаю.
Что касается того, как исправить объект OP... Ну, как бы я знал, не видя его код??? Дайте мне код, и я обещаю подумать об этом! Хорошо, я думаю об этом... Пока нет идей.
Ответ 2
Итак, проблема - класс, подобный чему-то ниже, ведет себя странно
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, но не уверен, что это поможет в проблеме 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
Ответ 3
Я думаю, ваша проблема в том, что вы пытаетесь расширить встроенный. Магические методы __
не вызываются для встроенных. Я думаю, вам придется сделать какой-то обертку и делегат, как это (непроверенный) (может быть, Анураг избил меня до удара):
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
'***'
Таким образом, определенно происходит некоторая несогласованность...