Fri, Jul 5, 2013
Often, when programming, we may want to change some already set behavior. This can be accomplished by sub-classing whatever classes we have and overriding those methods we are not happy with.
While this works with our own code, what happens if we want to change third party code? Of course we can edit it so that it instantiates one of our sub-classes, this however, can create another whole set of problems. What we need to do then, is to figure out a way to replace an object's methods with our own.
Probably the easiest way of adding a new method to an object or replacing an existing one is by patching its class. Say we want to teach our Dog class from the previous example how to howl, we can easily do it by defining a new howl function and adding it to our class like so:
While this is extremely easy to do, there's a couple of things we should be aware of. First of all, all instances of the modified class will be updated, meaning that not only new objects will have the new method definitions, but that all objects we created before patching our class will have them too (unless they haven't overridden the method themselves). Second, the new or modified methods will be bound, meaning that the first argument (i.e. 'self') will be the object being called.
Individual objects can also be patched without having to affect all other instances of its class. There is, however, a little gotcha when doing it. Lets look at the following example:
Now, let's try calling our newly defined method:
The problem with the previous code is that
herd is not a bound method, just take a look at the following code:
This means that the object being called is not passed as the first argument to the function, causing the error we previously saw. We can of course pass the instance ourselves, but that wouldn't work when replacing methods. The correct way of patching an object is by using the MethodType function in the types module like so:
As we can see the method is now bound and we can safely call it.
Replacing or adding methods at run-time can be extremely useful. While it is often used (e.g. sometimes functions for communicating with external services are replaced when unit-testing) it's extremely important to keep code maintainability in mind before deciding to do so.