mrocklin/multipledispatch — pull request #26 by eriknw (2014-07-11 20:12:14+00:00)
Title: Bind dispatched methods, and don't rely on "self" argument to infer methods

These two changes are not dependent on each other (sorry for having them in the same commit/PR). I don't know how solid either of the changes are, but I think they are worth exploring.

Previously, MethodDispatcher's `__get__` would be called each time a method was called. Moreover, the method dispatcher does not *look* like a method. So, we now bind the method dispatcher as a method when `__get__` is called, which should only happen once. This is similar to how regular functions are bound as methods in Python, although I don't know exactly *when* the magic normally occurs for regular methods.

The second tweak changes how methods are inferred. Previously, the signature was inspected, and it was determined to be a method if the first argument was named "self". This fails for functions declared outside of a class, or--goodness forbid--somebody doesn't use "self" as the first argument of a method. There is another way. In the standard Python data model, classes are the only objects that have the `__module__` attribute. Interestingly, this appears to be already defined in the local scope of the class definition. Hence, we can use `inspect` to check in `f_locals`.

In practice, "self" should *always* be used as the first item in method signatures. In other words, I'm not concerned about false-negatives (i.e., when "self" is not used for methods). This means that we now have two criteria, each of which may give false-positives, but not false-negatives. Therefore, we may want to check for both "self" in the signature *and* `__module__` in the `f_locals`.

Here's an example in Python 2.7 that illustrates how MethodDispatcher behaves like a method:
```python
In [1]: %paste
from multipledispatch import dispatch

class Test(object):
def __init__(self, x):
self.x = x

@dispatch(int)
def f(self, y):
return self.x + y

@dispatch(float)
def f(self, y):
return self.x - y
## -- End pasted text --

In [2]: test = Test(1)

In [3]: test.f(1)
Out[3]: 2

In [4]: test.f(1.0)
Out[4]: 0.0

In [5]: test.f
Out[5]: of <__main__.Test object at 0x2501550>>

In [6]: test.__getattribute__('f')
Out[6]: of <__main__.Test object at 0x2501550>>

In [7]: Test.f
Out[7]: >

In [8]: Test.__getattribute__(Test, 'f')
Out[8]:

In [9]: test.f is test.f
Out[9]: True
```
Finally, I found the original `test_methods_multiple_dispatch` test very confusing. It didn't use "self", and the functions were essentially static functions. Was this intended?