Reputation: 1293
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!
Upvotes: 11
Views: 10846
Reputation: 730
Hello The correct way to do this would be something like this you have a Metaclass > --- Decoratorclass > --> Actual Class
class Meta(type):
""" Metaclass """
def __call__(cls, *args, **kwargs):
instance = super(Meta, cls).__call__(*args, **kwargs)
return instance
def __init__(cls, name, base, attr):
super(Meta, cls).__init__(name, base, attr)
class counted(metaclass=Meta):
""" counts how often a function is called """
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Timer Start ")
Tem = self.func(*args, **kwargs)
print("Timer End {} ".format(Tem))
return Tem
class Test(object):
def __init__(self, *args, **kwargs):
pass
@counted
def methodA():
print("Method A")
return "1111"
if __name__ == "__main__":
obj = Test()
obj.methodA()
Upvotes: 0
Reputation: 1171
Here's a fix:
class myDecorator(object):
def __init__(self, f):
print ("inside myDecorator.__init__()")
self.f = f #Store the function being wrapped
def __call__(self):
print ("inside myDecorator.__call__()")
self.f() # Call the wrapped function
Upvotes: 0
Reputation: 184280
afunction
is being replaced by an instance of the class myDecorator
. The class has a __call__
method, so the instance can be called like a function. Thus, assuming its signature is compatible (usually a decorator returns something that uses *args
and **kwargs
), the instance can be used as a replacement for the original function.
Normally the __call__
method would call the wrapped function. Doing it in __init__
is generally incorrect; __init__
should store a reference to the wrapped function as an instance attribute, so that __call__
can call it.
Upvotes: 1
Reputation: 486
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.
Upvotes: 11
Reputation: 599796
That's the entire purpose of a decorator: to replace (or, more usually, wrap) a function with the function returned by the decorator. In your case, aFunction
is being replaced with an instance of myDecorator
, so when you call aFunction()
you are really calling that instance: and in Python, calling a class instance invokes its __call__
method.
Defining a decorated function is exactly equivalent to this:
def aFunction():
print("inside aFunction()")
aFunction = myDecorator(aFunction)
Usually, of course, the wrapping function would call the original function after doing whatever it does.
Upvotes: 2