Как динамически переопределить __setitem__? (без подкласса) - PullRequest
4 голосов
/ 14 февраля 2012

Я не могу переопределить некоторые встроенные функции, такие как __setitem__, в Python2.7 (хотя то же самое происходит в предыдущих версиях, которые я тестировал)

Хотя я знаюэто легко сделать с помощью подклассов, это не то, что я хочу здесь, мне нужно иметь возможность динамически переопределять эти методы.

Очевидно, когда мой класс является подклассом ' object ', переопределенный метод всегда заканчивается вызовом исходного, но когда мой класс не является' объектом ', он работает:

>>> def new_implementation(k, v):
...     print 'New implementation'
...

### class that extends object
>>> class ExtendsObject(object):
...     def __setitem__(self, k, v):
...             print 'ExtendsObject implementation'
...



### Checking implementation
>>> obj = ExtendsObject()
>>> obj[0]=0
ExtendsObject implementation

### trying to override setitem, no success
>>> obj.__setitem__ = new_implementation
>>> obj[0]=0
ExtendsObject implementation




### class that does NOT extends object
>>> class DoesNotExtend:
...     def __setitem__(self, k, v):
...             print 'DoesNotExtend implementation'
...

### Checking implementation
>>> obj_2 = DoesNotExtend()
>>> obj_2[0]=0
DoesNotExtend implementation

### overriding now works!
>>> obj_2.__setitem__ = new_implementation
>>> obj_2[0]=0
New implementation

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

Это ошибка?Я что-то не так делаю?

Ответы [ 2 ]

5 голосов
/ 14 февраля 2012

Для классов старого стиля в экземпляре каждый раз искались специальные методы. Классы нового стиля ищут специальные методы только для типа экземпляра, а не в самом словаре экземпляра - поэтому вы видите поведение, которое видите.

(Если вы не знаете, класс нового стиля - это класс, прямо или косвенно производный от object.)

2 голосов
/ 14 февраля 2012

Как говорит Свен Марнах , проблема заключается в том, что для классов нового стиля индексированное присваивание использует __setitem__, определенный классом .Но это не трудно обойти эту проблему.Решение, как это часто бывает, включает другой уровень косвенности .Это не очень хороший дизайн, но кажется очевидным способом сделать то, что вы хотите:

>>> class Foo(object):
...     def __setitem__(self, k, v):
...         return self._instance_setitem(k, v)
...     def _instance_setitem(self, k, v):
...         print 'old setitem'
... 
>>> def new_setitem(self, k, v):
...     print 'new setitem'
... 
>>> f[0] = 0
old setitem
>>> f._instance_setitem = types.MethodType(new_setitem, f, Foo)
>>> f[0] = 0
new setitem
...