Интерполяция строк 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
'***'

Таким образом, определенно происходит некоторая несогласованность...