If you are familiar with writing Django applications, you have probably run across the problem of extending the builtin User authentication model. Django does not yet have the hooks necessary for modifying the User object in a nice way, so you more or less have to resort to monkey patching.
Here is the basic monkey patching pattern I have seen:
def user_get_name(self): # do something with the user object which is self return "%s, %s" % (self.last_name, self.first_name) User.get_name = user_get_name
Or if it is really just a one liner you can use a lambda, which avoids dirtying up the local namespace of wherever you are performing the monkey patching:
User.get_name = lambda self: "%s, %s" % (self.last_name, self.first_name)
The first monkey patching pattern makes reading the code incredibly painful (at least to me) and the lambda pattern isn’t much better either.
You can perform the same operations in a more readable manner using decorators. Here is what it would look like:
def monkeypatch(cls): def decorator(f): setattr(cls, f.__name__, f) return decorator
Now to monkey patch the
get_name method of a
User object, you would do this:
@monkeypatch(User) def get_name(self): return "%s, %s" % (self.last_name, self.first_name)
I personally think this is a bit more readable. The real advantage to using a monkeypatch decorator though, is that you call out the fact that you are monkey patching. While reading the above code, it is very clear that monkey patching business is going on.
Monkey patching is almost never the best way to accomplish what you’re trying to do, but it will often get the job done fast. To remind yourself that you should revisit any monkey patching code later and think of a better way to do it, consider renaming the decorator to
Class decorators with python 2.6
If you are using python2.6, you can also use monkey patching decorators on entire classes. Here is an example of such a decorator:
def monkeypatch(cls_to_patch): def decorator(cls): cls_to_patch.__bases__ += (cls,) return cls return decorator
You would use this decorator like so:
@monkeypatch(User) class MyUser: def get_name(self): return "%s, %s" % (self.last_name, self.first_name) def get_initials(self): return self.first_name+self.last_name
The main caveat with this method is that
MyUser actually becomes a base class to
User so if
User ever gets a new method of the same name as one of your monkey patch methods, the
User version will take precedence. This might be a feature depending on what exactly it is you are doing.