'why and how __call__ is invoked in python decorators
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()"
def main():
print "main starts....."
aFunction()
print "main ends....."
Output :
inside myDecorator.__init__()
inside aFunction()
main starts.....
inside myDecorator.__call__()
main ends.....
I could not understand following points about above code:
Why "main starts...." is not first line to be printed?
Suppose if i'm returning some value from aFunction() then it would not be available in place of its call because
aFunction()is replaced withinside myDecorator.__call__()notinside aFunction().
Solution 1:[1]
The decorator syntax:
@myDecorator
def aFunction():
…
is equivalent to this:
def aFunction():
…
aFunction = myDecorator(aFunction)
If myDecorator is a class, then you would expect __init__ to get called when the function is defined. The instance of the class is then used in place of the function. When you call it, you would expect __call__ to be called, so that’s where the call to f() should go.
Solution 2:[2]
A function definition in Python
def foo():
pass
is actually a programmer-friendly way to say something like (pseudocode)*:
foo = function(code=compile('pass', ...), globals=globals(), name='foo')
so a wrapper simply gets in between:
foo = my_wrapper(function(...))
if the wrapper is a class, __init__ will get called. if it's a function, it will be called. After this statement, everything works as usual.
*this pseudocode is not that far from real code:
>>> def bar(): pass
...
>>> body = compile('print("hello")', '', 'exec')
>>> function = type(bar)
>>> foo = function(body, globals(), 'foo')
>>> foo()
hello
Solution 3:[3]
This is an old quesiton which I found by search. However, I find the accepted answer not entirely satisfactory.
Consider the normal way to write a decorator in Python:
def deco(f):
def g(*args, **kwargs):
print('boilerplate code')
return f(*args, **kwargs)
return g
@deco
def add(x, y):
return x + y
add(1, 2)
In this example, the line add(1, 2) is effectively calling deco(add)(1, 2).
Pay special attention to the way we write the line @deco: there are no parentheses in this line.
Now we write the equivalent decorator class with one argument added as the class attribute.
class Deco:
def __init__(self, x=1):
self.x = x
def __call__(self, f):
def g(*args, **kwargs):
print('boilerplate code, x is', self.x)
return f(*args, **kwargs)
return g
@Deco(5)
def add(x, y):
return x + y
add(1, 2)
Now the line add(1, 2) is effectively calling Deco(5)(add)(1, 2).
Notice that I have defined the x attribute as optional. This means we can write
@Deco()
def add(x, y):
return x + y
in this case.
Pay attention to the way we can write the line @Deco() this case, and the line @deco in the example above. They have very different meaning:
- one is
Deco()(add)(1, 2), - the other one is
deco(add)(1, 2).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Josh Lee |
| Solution 2 | |
| Solution 3 | Chuck Chen |
