Search code examples
pythonpython-decorators

Python Decorators - __call__ in class


I was trying to understand Python decorators and I was trying to write an equivalent program to this one:

class myDecorator(object):
   def __init__(self, f):
      print ("inside myDecorator.__init__()")
      f() # Prove that function definition has completed

   def __call__(self):
      print ("inside myDecorator.__call__()")

@myDecorator
def aFunction():
   print ("inside aFunction()")

print ("Finished decorating aFunction()")

aFunction()

The problem is I am not understanding how the __call__ method of class is being invoked by calling aFunction() in the end.

Is aFunction() being replaced by myDecorator.__call__(aFunction). Can you please help me? How would be an equivalent program without decorators?

Thanks!


Solution

  • The output of your code is

    inside myDecorator.__init__()
    inside aFunction()
    Finished decorating aFunction()
    inside myDecorator.__call__()
    

    First, do you know, what this @ decorator syntax mean?

    @decorator
    def function(a):
        pass
    

    is just another way of saying:

    def function(a):
        pass
    function = decorator(function)
    

    So, in Your case

    @myDecorator
    def aFunction():
       print ("inside aFunction()")
    

    means just

    def aFunction():
        print ("inside aFunction()")
    aFunction = myDecorator(aFunction)
    

    At first, You basically create a new instance of myDecorator class, invoking it's constructor (__init__) and passing to it a aFunction function object. Then, it executes print and given function. Also, note that this is happening while the function is loaded by interpreter, not when it's executed, so in case you import something from this file, it will execute then, not on use or call.

    Then, executing the aFunction(), when aFunction is still refering to the myDecorator instance, makes it to call the __call__ method of myDecorator, which executes. Note, that f() means the same as f.__call__(f) in this case, as __call__ method is used to enable and override default object's calling behaviour (in simplification, any object is callable when it has __call__ method defined).

    If you want to execute aFunction when it's called, then you should assign it to instance variable in __init__ and invoke it in __call__ of myDecorator.