One of the great features of Python is the ability to define methods outside of classes. For example, we can define a function which increments the attribute x
and add it to a Point
class:
def incx(self): self.x += 1 class Point(object): def __init__(self, x): self.x = x incx = incx
We can then create a point at the origin and increment x
:
In [2]: p = Point(0) In [3]: p.incx() In [4]: print p.x 1
The same code which defines Point
continues to work if we move incx
to another file, say demo.py
, and import it using from demo import incx
.
But if we were to put incx
in demo.pyx
and compile it (using python setup.py build_ext --inplace
), we get a strange error when running our simple test:
$ python bad.py Traceback (most recent call last): File "bad.py", line 10, in <module> p.incx() TypeError: incx() takes exactly one argument (0 given)
Read on to learn about instance methods and see how I fixed this.
My first thought was to write a simple wrapper function, using functools.wraps to preserve the docstring and name. This proved to be a clumsy solution.
A better approach turned out to be to use types.MethodType
to convert our built-in function into an (unbound) instance method. The docstring for MethodType
is pretty short:
instancemethod(function, instance, class)
The first and third arguments are obvious. For the second argument we’ll use None
since we have no instance of the class yet, making this an unbound instance method.
So to fix our simple example, we need only replace the incx = incx
line in the class definition with a short snippet after the class is defined:
from types import MethodType Point.incx = MethodType(incx, None, Point)
We can give this new version a try and see that it works:
$ python good.py
1
As always, the entire example is available as a Gist.