Замена __str__ в новом стиле

4

Я конвертирую старый код Python и заменяю некоторые классы новыми классами стилей. Проблема в том, что это нарушило поведение замены __str__, и я понятия не имею, почему.

class OldStyle():
    def __str__(self):
        return 'original'

old = OldStyle()
print old
old.__str__ = lambda: 'modified'
print old


class NewStyle(object):
    def __str__(self):
        return 'original'

new = NewStyle()
print new
new.__str__ = lambda: 'modified'
print new

Я ожидал

original
modified
original
modified

но я получил

original
modified
original
original

То есть, __str__ не был правильно заменен в новом стиле. Печать new.__str__ возвращает новую лямбду правильно, но str(new) все еще не вызывает ее. Я подумал, что это проблема кэширования поиска метода, но это происходит, даже если объект никогда не печатался раньше.

Почему это происходит? Я никогда не слышал об этой поведенческой разнице, и это происходит только с __str__, другие методы заменены штрафом.

  • 0
    Замена последней модификации на NewStyle.__str__ = lambda self: 'modified' работает как положено. Я думаю, что порядок разрешения методов различен для классов старого и нового стиля.
  • 0
    Интересно. Проблема в том, что мне нужно заменить __str__ только в этом единственном экземпляре. Я думаю, что могу реализовать это по-другому, но мне стало любопытно.
Показать ещё 3 комментария
Теги:
python-2.7

2 ответа

3
Лучший ответ

Это описано в модели данных python под имена специальных методов. В частности:

Например, если класс определяет метод с именем __getitem__, а x является экземпляром этого класса, то x[i] примерно эквивалентен x.__getitem__(i) для классов старого стиля и type(x).__getitem__(x, i) для нового стиля классы.

Я считаю, что это позволяет классам нового стиля быть немного более эффективными с точки зрения этих операций, потому что со старыми классами стилей python был вынужден искать атрибут, а затем вызывать атрибут с классами нового стиля, python может просто ссылаться на него как часть C-структуры где-то эффективно, вызывая поиск и вызовы в собственный C-код. Например:

class Foo:
    def __add__(self,other):
        return 4 + other

class Bar(object):
    def __add__(self,other):
        return 4 + other

import timeit
print timeit.timeit('f + 5','from __main__ import Foo; f = Foo()')
print timeit.timeit('b + 5','from __main__ import Bar; b = Bar()')

Для меня (python2.7.3, OS-X 10.5.8) класс нового стиля почти в 4 раза быстрее!

2.27801704407
0.602614879608
3

Это работает:

NewStyle.__str__ = lambda self: 'modified'

Кажется, что мы видим, что метод __str__ привязан к типу класса сейчас, а не к экземпляру. Я не знаю, почему это так. Вы можете явно вызвать new.__str__() и получить замененный метод, но str(new) всегда вызывает NewStyle.__str__(new).

  • 0
    Но это меняет поведение всех экземпляров NewStyle .
  • 0
    Конечно, это так. Возможно, вам следует создать производный класс и создать его экземпляр для одного экземпляра, для которого требуется другое поведение в строке.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню