14

I have several objects of different kinds (different function names, different signatures) and I monkey patch them to have a common way to access them from different functions. Briefly, there is a dispatcher that takes the objects that I want to patch and depending on the object type it calls different patcher. A patcher will add methods to the object:

def patcher_of_some_type(object):

    def target(self, value):
        # do something and call self methods

    object.target = types.MethodType(target, object)

    # many more of these

As the program grows more complicated making a wrapper around the object (or the object class) seems to be better idea. Some patchers share common code or are interrelated. But I do not control the object creation, nor the class creation. I only get the objects. And even if I could do that, I just want to wrap (or patch) certain objects, not all.

One solution might be add a base class to an existing object, but I am not sure how maintainable and safe this is. Is there another solution?

senderle
  • 145,869
  • 36
  • 209
  • 233
Hernan
  • 5,811
  • 10
  • 51
  • 86
  • So the classes are not defined from you? – dav1d Jun 14 '12 at 23:05
  • No, the classes are defined in a library which I do not control. I could create wrappers from all classes, but this will only work for the objects I instantiate, not for the ones instantiated by the library. If I patch the library, the converse happens: all the objects are patched and I only want to patch some of those. Finally, if I wrap some objects I would need keep my own datastructure to refer to them. That's why now I am patching the objects that I want. – Hernan Jun 14 '12 at 23:09
  • 1
    I would define a wrapper class to hold the objects. The need to have different kinds of wrappers that share common code can be handled by having a base wrapper class and deriving specialized versions from it. Basic OOP stuff... – martineau Jun 15 '12 at 00:43
  • @martineau: I don't understand your suggestion in relation to my problem. I am suggesting creating a wrapper class (an their specialized) versions, what I am asking is how to add all these class methods to an existing object in a robust and maintainable way. – Hernan Jun 15 '12 at 07:21
  • @Hernan: The whole idea of a wrapper class is that it acts as a [proxy](http://en.wikipedia.org/wiki/Proxy_pattern) for or interface to some underlying object (delegate), hence there shouldn't be a need to add methods to that object (which most languages can't do anyway). Besides that, it may not be possible, generally speaking, to do what you ask in a "robust and maintainable way" therefore doing so even when you can should be avoided. – martineau Jun 15 '12 at 14:00
  • @Hernan: There's reasons it's called "monkey patching". See [Monkey Patching - Pitfalls](http://en.wikipedia.org/wiki/Monkey_patch#Pitfalls). – martineau Jun 15 '12 at 14:08

3 Answers3

15

Dynamically modifying an object's type is reasonably safe, as long as the extra base class is compatible (and you'll get an exception if it isn't). The simplest way to add a base class is with the 3-argument type constructor:

cls = object.__class__
object.__class__ = cls.__class__(cls.__name__ + "WithExtraBase", (cls, ExtraBase), {})
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • 3
    using type (and please just use `type` instead of `object.__class__.__class__`, which btw fails with old-style classes!) is only neccessary if you need to set the class name, otherwise you can just do `class AnyNameHere(cls, ExtraBase): pass` which is more readable. – l4mpi Jun 15 '12 at 13:03
8

Based on a comment on another answer, this is another way to patch the base class:

class Patched(instance.__class__, ExtraBase):
    pass

instance.__class__ = Patched
Community
  • 1
  • 1
Jace Browning
  • 11,699
  • 10
  • 66
  • 90
  • 4
    This is really neat! It does seem to change the class name to Patched, however. You can get around that by adding a line: `Patched.__name__ = instance.__class__.__name__`. – paulmelnikow Feb 19 '15 at 02:56
4

To add a class to another classes bases, you could do something like this:

def pacher_of_some_type(object):

    #this class holds all the stuff you want to add to the object
    class Patch():
        def target(self, value):
            #do something

    #add it to the class' base classes
    object.__class__.__bases__ += (Patch, )

But this will affect ALL objects of this class as you actually change the class itself, which doesn't seem to be what you want... if you just want to patch an Instance, you could subclass the original class and change the instances class dynamically:

class PatchedClass(object.__class__):
    def target(self, value):
        #do whatever

object.__class__ = PatchedClass

In terms of safety and manageability, I'd say dynamically adding a base class is as maintainable as dynamically adding some functions, and safer because you're less likely to accidentally overwrite some internal functions of the original class because the new base class is added to the end of the tuple, meaning it is used last for name resolution. If you actually want to overwrite methods, add them to the beginning of the base classes tuple (of course this doesn't apply to the subclass approach). Don't ask me if dynamically changing the class of an instance is safe at all, but in a trivial example it didn't seem to be a problem:

class A():
    def p(self): print 1

class B():
    def p(self): print 2

a = A()
a.p()    #prints 1
a.__class__ = B
a.p()    #prints 2
l4mpi
  • 5,103
  • 3
  • 34
  • 54