Search

13 March, 2019

Python Special Methods : __call__

Python has some methods that are special in a way . If  these methods are created inside a class, they can bring in some unique behaviors . We will talk about the method '__call__' today.


When we create a class (old/new style), and an instance of that class, that instance has certain ...features by default. 

class MyClass:
    pass

m = MyClass()
print dir(m)


Consider above class . Things to note:
  • I am using Python 2..7 . So above class is an old-style class (since I did not inherit 'object' into it).
  • The class has no methods defined in it. 
Now lets run this program. Here's the output:


['__doc__', '__module__']

Let's try calling the instance 'm' with parenthesis.


>>> m
<__main__.MyClass instance at 0x000000000332E488>
>>> m()

Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    m()
AttributeError: MyClass instance has no __call__ method

What happened here. It threw an Attribute Error saying I don't have a __call__ method.

BUT I NEVER called the method __call__ anywhere in my program. Then why is it giving me this error statement.

Turns out. That's what is looked up inside a class when someone tries to call an instance with arguments using parenthesis.

Another question. What's about new style classes. Do they also need them explicitly. Let's change our class a bit.


class MyClass(object):
    pass

m = MyClass()

Things to note:  Now we have inherited object into our class making it a new-style class.


>>> m()

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    m()
TypeError: 'MyClass' object is not callable

Seems like, we DO NEED to declare the __call__ method. But the Error type has changed now and the message too.

Lets now try to add this method and see what happens.



class MyClass(object):
    def __call__(self, *args):
        return sum(args)   

m = MyClass()
print m(1,2,3,4,5)

>>>15

Now if the above analogy hold true all throughout Python, that means functions must be having this method by default. Let's find if it's true.




>>> def myFun(num, power):
 return num**power

>>> myFun(2, 4)
16
>>> dir(myFun)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

>>> myFun.__call__(2,4)
16

As suspected, every function ALWAYS will have a __call__ method supported since all functions are callable. 

No comments:

Post a Comment