treecoder
treecoder

Reputation: 45171

Calling a function as a method of a class

My code:

def func(*x):
    print('func:', x)


class ABC:
    def __init__(self, f):
        self.f1 = f

    def f2(*x):
        print('f2:', x)

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>

func is a normal function and I am making it a method f1 of the class.

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. If I 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 calling it as a method of a class? And how does Python know that I am calling an outer function as a method so that it should not pass an instance to it?

Upvotes: 4

Views: 356

Answers (2)

Paulo Scardine
Paulo Scardine

Reputation: 77369

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 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.

Upvotes: 7

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 251136

That's because f1 and f3 are not class method they are just references to a global function defined in __main__:

In [5]: a.f1
Out[5]: <function __main__.func>

In [8]: a.f3
Out[8]: <function __main__.func>

In [9]: a.f2
Out[9]: <bound method ABC.f2 of <__main__.ABC instance at 0x8ac04ac>>

you can do something like this to make a global function a class method:

In [16]: class ABC:
    def __init__(self,f):
        ABC.f1=f
    def f2(*x):    
        print('f2',x)
   ....:         

In [17]: a=ABC(func)

In [18]: a.f1(10)
('func:', (<__main__.ABC instance at 0x8abb7ec>, 10))

Upvotes: 3

Related Questions