Python: calling a function as a method of a class
Question: Let's start with some code:

    
    
    def func(*x):
        print('func:', x)
    
    
    class ABC:
        def __init__(self, f):
            self.f1 = f
    
        def f2(*x):
            print('f2:', x)
    

Now we do some tests:

    
    
    >>> a = ABC(func)
    >>> a.f1(10)
    func: (10,)
    >>> a.f2(10)
    f2: (<__main__.ABC object at 0xb75381cc>, 10)
    >>> a.f3 = func
    >>> a.f3(10)
    func: (10,)
    >>> a.f1
    <function func at 0xb74911ec>
    >>> a.f2
    <bound method ABC.f2 of <__main__.ABC object at 0xb75381cc>>
    >>> a.f3
    <function func at 0xb74911ec>
    

Note that `func` is a normal function and we are making it a method `f1` of
the class.

We can see that `f2` is getting the class instance as the first argument, but
`f1` and `f3` are not, even though all functions are called as class methods.
We can also see that if we call a normal function as a method of a class,
Python does not make a bound method from it.

So why is `f1` or `f3` NOT getting a class instance passed to it even when we
are calling it as a method of a class? And also, how does Python know that we
are calling an outer function as a method so that it should not pass an
instance to it.

**\-- EDIT --**

OK, so basically what I am doing wrong is that I am attaching the functions on
the instance and NOT on the class object itself. These functions therefore
simply become instance attributes. We can check this with:

    
    
    >>> ABC.__dict__
    ... contents...
    >>> a.__dict__
    {'f1': <function func at 0xb74911ec>, 'f3': <function func at 0xb74911ec>}
    

Also note that this dict can not be assigned to:

    
    
    >>> ABC.__dict__['f4'] = func
    TypeError: 'dict_proxy' object does not support item assignment
    


Answer: You kind of partially answered your own question inspecting the object. In
Python, objects behave like namespaces, so the first attribute points to a
function and the second points to a method.

This is how you can add a method dynamically:

    
    
    from types import MethodType
    
    def func(*x):
        print('func:', x)
    
    
    class ABC:
        def __init__(self, f):
            self.f1 = MethodType(f, self, self.__class__)
    
        def f2(*x):
            print('f2:', x)
    
    if __name__ == '__main__':
        a = ABC(func)
        print a.f1(10)
        print a.f2(10)
        a.f3 = MethodType(func, a, ABC)
        print a.f3(10)
    

Note that it will bind the method to your instance, not to the base class. In
order to [monkeypatch](http://en.wikipedia.org/wiki/Monkey_patch) the ABC
class:

    
    
    >>> ABC.f4 = MethodType(func, None, ABC)
    >>> a.f4(1)
    ('func:', (<__main__.ABC instance at 0x02AA8AD0>, 1))
    

Monkeypatching is usually frowned upon in the Python circles, despite being
popular in other dynamic languages (notably in Ruby when the language was
younger).

If you ever resort to this powerful yet dangerous technique, my advice is:

  * never, ever override an existing class method. just don't.

