I had this idea for a project I could do, but I thought “maybe I should do this other thing first to get to grips with the API”. I was doing something for that project, and got distracted doing a simpler project to learn about the technologies and tools involved. I got distracted from that project by some weird behaviour of a class in the python standard library (I’ll post about that later). And I got distracted from that distraction by trying to find convenient ways to explore the code of python objects so as to make subclassing them easier. And this post is about this distraction to a distraction to a distraction. With any luck, I will eventually post about all of these things, but I’m not naming them in case I never get around to doing them.
So you want to subclass an object in python. Ideally, if you’re going to make a subclass, you want to see the code for the original object to use as a scaffold when you start over-riding things. You could dig around to try to find where that object is defined, but that’s hit-and-miss, and kind of awkward if you only want to override one or two methods. Particularly if that object is inheriting some methods from its parents etc etc.
Python has a great module called inspect
which basically gives you a bunch of tools to
make this sort of thing way easier.
In my fiddling around, I found the following wrapper around inspect
kind of useful.
import inspect
import pyperclip
class MethodInspector:
def __init__(self, obj):
self.obj = obj
self.methods = dict(inspect.getmembers(obj))
def _get_source(self, method_name):
return inspect.getsource(self.methods[method_name])
def _get_parents(self):
return type(self.obj).__bases__
def print_source(self, method_name):
print(self._get_source(method_name))
def copy_source(self, method_name):
pyperclip.copy(self._get_source(method_name))
def print_parents(self):
print("\n".join([x.__name__ for x in self._get_parents()]))
def show_methods(self):
print("\n".join(self.methods.keys()))
What does it do?
You instantiate a MethodInspector
by passing it an instance of the object you
want to explore (the object you want to subclass in my case).
Then show_methods()
will print out a list of the methods defined for that object.
This includes all methods it inherits from its parents.
Then we have print_source(method_name)
which
prints the source code for the method called method_name
.
(You pass in the method name as a string).
You can use copy_source(method_name)
to copy the source for a method
to the clipboard, which you can then paste into your subclass.
This makes use of the pyperclip
module
which gives you a simple way to have python interact with your clipboard.
Finally, print_parents()
prints out the parent classes of the object.
This doesn’t use the inspect
module, but it’s also a useful thing to look at
on occasion.
The limitation with this at the moment is that if you hit a method defined
in the built in core of the language
(for example, dict.__setitem__
)
then inspect
will error out.
But if that happens, this probably means that the object is defined in C rather
than in python itself.
For example, here’s the
github page for the defintion of the dictionary object.