2010-02-22 3 views
13

Что может вызвать следующее поведение?Интерполяция строки 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 версии пакетов и код, используемый являются:

EDIT INFO:

  • добавлены/обновлены имена методов, которые были переопределены
  • добавил некоторую информацию (питон версия, и второстепенная информация)
  • обновил неверную информацию (класс `msg` основан на классе` unicode`, а не `basestring`)
  • добавил фактическое исполнение иона класса используется
+3

@extraneon: это питон 2 .x: 'print' - это инструкция, basestring, unicode! – SilentGhost

+1

изменение значения переменной 'msg' между' print' s объясняет это – van

+1

У вас есть фактический код для этого объекта? (Или, скорее, его класс.) Было бы полезно, если бы вы могли вставить его здесь ... –

ответ

8

Update 2: Пожалуйста, найти оригинальный ответ, в том числе простой пример класса, проявляющего поведение, описанное в OP, ниже горизонтальной панели. Что касается того, что я смог догадаться в ходе моего исследования источников Python (версия 2.6.4):

Файл Include/unicodeobject.h содержит следующие строки (nos.436-7 в моем (несколько старый) выписка):

#define PyUnicode_AS_UNICODE(op) \            
     (((PyUnicodeObject *)(op))->str) 

Это используется повсюду в коде форматирования, который, насколько я могу судить, означает, что во время форматирования строки, любой объект, который наследует от unicode будет достигнуто так, что его буферный ряд в unicode может использоваться напрямую, не вызывая никаких методов Python. Что хорошо в плане производительности, я уверен (и очень согласен с гипотезой Юргена в комментарии к этому ответу).

Для вопроса ОП это, вероятно, означает, что все работает так, как бы им хотелось, возможно, только в том случае, если что-то вроде идеи класса обертки Anurag Uniyal приемлемо для данного конкретного варианта использования. Если это не так, единственное, что приходит мне на ум, это обернуть объекты этого класса в str/unicode, где бы их не было интерполировано в строку ... тьфу. (Я искренне надеюсь, что я просто отсутствует более чистое решение, которое кто-то укажут в минуту!)


(Update: Это было отправлено около минуты до того, как OP включен код своего класса, но я оставляю его здесь в любом случае (1) для гипотезы/первоначальной попытки объяснения ниже кода, (2) для простого примера того, как произвести это поведение (Anurag Uniyal с тех пор предоставил другой, вызывающий конструктор unicode напрямую, а не через super), (3) в надежде, что позже сможет редактировать что-то, чтобы помочь ОП в получении желаемого поведения.)

Вот пример класса, который на самом деле работает как то, что описывает 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 ... Ну, как бы я знал, не видя его код ??? Дайте мне код, и я обещаю подумать об этом! Хорошо, я думаю об этом ... Пока нет идей.

+0

Похоже на то, что печать сделала несколько ярлыков - чтобы ускорить работу, я бы подумал. Python имеет (относительно быстрые) внутренние интерфейсы и (относительно медленные) внешние интерфейсы. Я думаю, что кто-то пытался избежать накладных расходов ... – Juergen

+0

@Juergen: Включена какая-то информация о том, как выглядят источники в ответе сейчас ... Кажется, вы правы. –

+0

@Michal: Спасибо за информацию! Python довольно чист как система, но (насколько я понимаю, а также видел немного), иногда некоторые быстрые клавиши создаются внутри, где можно получить большое преимущество в скорости. На мой взгляд, это нормально, так как эти ярлыки не видны в 99% всех случаев ... в других 1%, обходной путь должен быть сделан, как в этом случае. Конечно, когда забивает один, это может быть довольно неожиданным или даже раздражающим ... – Juergen

6

Так проблема в том, класс, как к чему-то ниже ведет себя странно

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 
+0

Я не могу позволить себе изменить исходный код на unicode. Однако, спасибо за упрощенный пример. – vaab

+0

@vaab: если вы посмотрите на расширенный ответ, который я дал, добавление '__getattr__' будет перенаправлять все аксессоры, которые * были бы * разрешены путем наследования содержащегося атрибута .msg. Это очень мощная идиома в Python и помещает wrap-and-delegate наравне с наследованием с меньшим сцеплением. – PaulMcG

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) 

Update 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 
'***' 

Так что, безусловно, некоторое несоответствие происходит ...

+0

Отличная идея, но это не работает! ;) Ну, это работает для данного доктрины, но тот факт, что этот класс больше не является экземпляром 'string', прерывает некоторые другие проверки C в общих библиотеках Python, которые я использую и которые нужно использовать. Завтра я буду более ясным. – vaab

+0

Ах, вы (или те libs) используете isinstance, возможно? И теперь этот класс больше не наследует от basestring? Хммм, эти проверки не будут делать проверки параметров, не так ли? Это отличный пример, показывающий, почему проверка параметров ininstance не всегда лучшая идея в Python. – PaulMcG