Why can't I Monkey-patch __setitem__ on a subclass of collections.MutableMapping

Viewed 268

Usually, I can willy-nilly monkey-patch and mock methods:

from UserDict import DictMixin
class py2fake_dict(DictMixin):
    def __setitem__(self, name, value):
        raise AssertionError("Don't talk to me!")
    def __delitem__(self, name):
        pass
    def __getitem__(self, name):
        pass
    def __iter__(self):
        yield None
    def __len__(self):
        return 0

c = py2fake_dict()
c.__setitem__ = lambda name, value: 'All clear.'
# This is OK:
c[1] = 2

However, if the method in question is on a MutableMapping subclass, I cannot:

from collections import MutableMapping
class py3fake_dict(MutableMapping):
    def __setitem__(self, name, value):
        raise AssertionError("Don't talk to me!")
    def __delitem__(self, name):
        pass
    def __getitem__(self, name):
        pass
    def __iter__(self):
        yield None
    def __len__(self):
        return 0

c = py3fake_dict()
c.__setitem__ = lambda name, value: 'All clear.'
# This hits the assertion!!!
c[1] = 2

I found this in working code when upgrading from UserDict.DictMixin to collections.MutableMapping as part of our Python3 upgrade. I'll remove the code, or use subclass in the future, but I just want to understand what is happening.

1 Answers

You can monkey-patch methods on the class just fine, but monkey-patching magic methods on instances of a new-style class hasn't worked since new-style classes were introduced back in Python 2.2. New-style classes are object and its descendants, and for many reasons (better performance, less weird edge cases, support for new features like super and property setters, Python 3 compatibility), you should prefer new-style classes over old-style whenever possible.

UserDict.DictMixin is an old-style class. Setting magic methods on instances only works on instances of old-style classes. collections.MutableMapping is a new-style class.

In Python 3, there are no old-style classes, so you're going to have to get used to magic methods only working if set on a type.

Related