Попытка изменить метод __unicode__ в экземпляре после его создания приводит к различным результатам в Python 2.5 и 2.6.

Вот тестовый сценарий:

class Dummy(object):

    def __unicode__(self):
        return u'one'

    def two(self):
        return u'two'

d = Dummy()
print unicode(d)
d.__unicode__ = d.two
print unicode(d)
print d.__unicode__()

На Python 2.5 это производит

one
two
two

То есть, изменение __unicode__ экземпляра также приводит к изменению unicode(instance)

На Python 2.6 это производит

one
one
two

Итак, после изменения unicode(instance) и instance.__unicode__() возвращают разные результаты.

Почему? Как я могу заставить это работать на Python 2.6?

(Для этого стоит использовать случай, когда я хочу добавить что-то к выводу __unicode__ для всех подклассов данного класса, без необходимости изменять код подклассов.)

Измените его, чтобы сделать его использование более понятным

У меня есть класс А, который имеет много подклассов. Эти подклассы определяют простые __unicode__ методы. Я хочу добавить логику, чтобы для экземпляров подкласса класса A юникод (экземпляр) получал что-то прикрепленное до конца. Чтобы сделать код простым, и поскольку есть много подклассов, которые я не хочу менять, я бы предпочел избегать редактирования кода подкласса.

Это фактически существующий код, который работает в Python 2.5. Это что-то вроде этого:

class A(object):

    def __init__(self):
        self._original_unicode = self.__unicode__
        self.__unicode__ = self.augmented_unicode

    def augmented_unicode(self):
        return self._original_unicode() + u' EXTRA'

Именно этот код больше не работает на 2.6. Любые предложения о том, как достичь этого без изменения кода подкласса? (Если ответ включает метаклассы, обратите внимание, что класс A сам является подклассом другого класса - django.db.models.Model - с довольно сложным метаклассом.)

4
michael 3 Авг 2010 в 00:00

3 ответа

Лучший ответ

Похоже, вам не разрешено методы протокола обезьяньего патча ( те, которые начинаются и заканчиваются двойным подчеркиванием):

Примечание

На практике есть еще одно исключение что мы не справились здесь. Хотя вы можете переопределить методы с экземпляром атрибуты (очень полезно для обезьяны методы исправления в тестовых целях) вы не можете сделать это с Python Методы протокола. Это «магия методы, чьи имена начинаются и заканчиваются с двойным подчеркиванием. Когда вызывается интерпретатором Python они посмотрел прямо на класс и не в случае (однако, если вы ищите их напрямую - например, x. repr - обычный поиск атрибутов правила применяются).

В таком случае вы можете застрять, если не можете использовать ~ ответ УНУТБУ.

РЕДАКТИРОВАТЬ . Вы также можете использовать метод базового класса __unicode__ для поиска вхождения объекта экземпляра для атрибута __unicode__. Если он присутствует, то __unicode__ определен для объекта экземпляра, и метод класса вызывает метод экземпляра. В противном случае мы вернемся к определению класса __unicode__.

Я думаю , что это может позволить вашему существующему коду подкласса работать без каких-либо изменений. Тем не менее, становится уродливым, если производный класс хочет вызвать реализацию класса - вы должны быть осторожны, чтобы избежать бесконечных циклов. Я не реализовывал такие хаки в этом примере; просто прокомментировал о них.

import types

class Dummy(object):
    def __unicode__(self):
        func = self.__dict__.get("__unicode__", None)
        if func:
            // WARNING: if func() invokes this __unicode__ method directly,
            // an infinite loop could result. You may need an ugly hack to guard
            // against this. (E.g., set a flag on entry / unset the flag on exit,
            // using a try/finally to protect against exceptions.)

            return func()

        return u'one'

    def two(self):
        return u'two'

d = Dummy()
print unicode(d)
funcType = type(Dummy.__unicode__)
d.__unicode__ = types.MethodType(Dummy.two, d)
print unicode(d)
print d.__unicode__()

Тестирование с Python 2.6 дает следующий результат:

> python dummy.py 
one
two
two
2
Community 23 Май 2017 в 12:13

Похоже, Дэн прав насчет методов протезирования патчей, и это было изменение между Python 2.5 и Python 2.6.

Мое исправление закончилось тем, что я внес изменения в классы, а не в экземпляры:

class A(object):
    def __init__(self):
        self.__class__.__unicode__ = self.__class__.augmented_unicode
0
michael 2 Авг 2010 в 21:33

Редактировать . В ответ на комментарий ОП: добавление уровня косвенности может позволить вам изменить поведение unicode для каждого экземпляра:

class Dummy(object):

    def __unicode__(self):
        return self._unicode()

    def _unicode(self):
        return u'one'

    def two(self):
        return u'two'

d = Dummy()
print unicode(d)
# one
d._unicode = d.two
print unicode(d)
# two
print d.__unicode__()
# two
2
unutbu 2 Авг 2010 в 20:22