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

Спасибо за все предложения.

Я не чувствую, что это поможет больше, но для любопытных читателей (и предприимчивый 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 и используемый код:

ИНФОРМАЦИЯ О РЕДАКТИРОВАНИИ:

  • добавили/обновили названия методов, которые были переопределены
  • добавленный еще некоторая информация (версия Python и незначительная информация)
  • обновленный некоторая неправильная информация (класс 'сообщения' основан на 'unicode' классе и не 'basestring'),
  • добавленный фактическая реализация класса используется

13
задан vaab 22 February 2010 в 16:24
поделиться

3 ответа

Обновление 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. Как это происходит внутри, почему это так работает и является ли это ошибкой или особенностью, я действительно не знаю.

Что касается того, как исправить объект ОП... Ну, откуда мне знать, не видя его кода? Дайте мне код, и я обещаю подумать об этом! Хорошо, я думаю об этом... Пока никаких идей.

8
ответ дан 1 December 2019 в 23:47
поделиться

Я думаю, ваша проблема в том, что вы пытаетесь расширить встроенный. Методы 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
'***'

Так что определенно происходит некоторая несогласованность ...

3
ответ дан 1 December 2019 в 23:47
поделиться

Итак, проблема в том, что класс похож на то, что ниже, ведет себя странно

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
6
ответ дан 1 December 2019 в 23:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: