Decorator Invoked Automatically - python

I am trying to understand the logic behind two versions of my script which has decorators used in it. In one of the script, the decorator is invoked automatically without being even called.
Script 1:
def outer_decorator(input_function):
def decorator(*args, **kwargs):
print "Outer Decorator Called"
output = input_function(*args, **kwargs)
return output
return decorator
class X:
#outer_decorator
def inner_decorator(input_function):
def decorator(*args, **kwargs):
output = input_function(*args, **kwargs)
print "Inner Decorator Called"
return output
return decorator
def z(self, a, b):
"""
Function returns a - b
"""
return a - b
x = X()
print(x.z(1, 2))
Output
-1
So whenever I run Script 1, I am creating an object of class X and then calling function z. So, in this case, the inner decorator function is not being used anywhere and hence it is not invoked.
Script 2:
def outer_decorator(input_function):
def decorator(*args, **kwargs):
print "Outer Decorator Called"
output = input_function(*args, **kwargs)
return output
return decorator
class X:
#outer_decorator
def inner_decorator(input_function):
def decorator(*args, **kwargs):
output = input_function(*args, **kwargs)
print "Inner Decorator Called"
return output
return decorator
#inner_decorator
def s(self, a, b):
"""
Function return a + b
"""
return a + b
def z(self, a, b):
"""
Function returns a - b
"""
return a - b
x = X()
print(x.z(1, 2))
So now in the above Script I have introduces another function s which uses #inner_decorator. But I am still only invoking function z of class x. But when I run this script I get the following output.
Outer Decorator Called
-1
So why is #outer_decorator called and is printing? Is it because the decorator used somewhere is processed by the interpreter?

The reason the outer_decorator has been invoked is because of the fact that you are decorating a decorator itself.
def outer_decorator(input_function):
def decorator(*args, **kwargs):
print "Outer Decorator Called"
output = input_function(*args, **kwargs)
return output
return decorator
class X:
#outer_decorator
def inner_decorator(input_function):
def decorator(*args, **kwargs):
output = input_function(*args, **kwargs)
print "Inner Decorator Called"
return output
return decorator
This expression #outer_decorator actually translates to inner_decorator = outer_decorator(inner_decorator),so in your code though you have not explicitly called the function that you decorated with inner_decorator but the moment you decorated the function s with inner_decorator it actually called the decorated version of inner_decorated which in turn called the decorator function defined inside the outer_decorator.
I hope this makes the things clear.

Script 1:
inner_decorator = outer_decorator(inner_decorator) # i.e. a function
# object, no invocation
Script 2:
Here:
#outer_decorator
def inner_decorator(input_function):
this gets invoked:
inner_decorator = outer_decorator(inner_decorator) # i.e. a function
# object, no invocation
But,
here:
#inner_decorator
def s(self, a, b):
this leads to:
s = inner_decorator(s) = outer_decorator(inner_decorator)(s) # causes
# invocation of outer_decorator

Related

What's internal mechanism of decorator in Python

I know that the decorator is a function that takes another function and extends its behavior.
In the example below, I previously assume that test() now is effectively equivalent to decorator(test)().
def decorator(func):
def wrapper(*args, **kwargs):
...
res = func(*args, **kwargs)
...
return res
return wrapper
#decorator
def test():
pass
However, after adding a function attribute in the decorator and run both test() and decorator(test)(), the results are different. It seems that in the case of decorator(test)(), the decorator function is indeed ran so that num is reset; when using #decorator instead, the decorator function is not ran as I expected?
def decorator(func):
decorator.num = 0
def wrapper(*args, **kwargs):
...
res = func(*args, **kwargs)
...
return res
return wrapper
#decorator
def test():
pass
def test2():
pass
decorator.num = 5
test()
print(decorator.num)
decorator.num = 5
decorator(test2)()
print(decorator.num)
---------------------
5
0
Your confusion stems from when the decorator runs. The syntax
#decorator
def foo(): ...
is equivalent to
def foo(): ...
foo = decorator(foo)
That is, immediately after the function is defined, the decorator is called on it, and the result of calling the decorator is assigned back to the original function name. It's called only once, at definition time, not once per function call.
The same is true of classes. The syntax
#decorator
class Foo: ...
is equivalent to
class Foo: ...
Foo = decorator(Foo)

Python Decorator in Inheritance

I have the following code:
class Foo:
iterations = 3
class Bar(Foo):
#test_decorator(<????>)
def hello(self):
print("Hello world!")
def test_decorator(input):
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
for _ in range(input):
func(*args, **kwargs)
print("Something is happening after the function is called.")
return wrapper
return my_decorator
I would like to pass my iterations variable which is in the parent class to the decorator test_decorator which is in my child class, instead of <????>
I tried the following ways:
self.iteration doesn't work since we don't have access to self
Foo.iterations doesn't work because it will act as a constant, if we change iterations "hello world" will be displayed only 3 times instead of 5 (as in the example below)
Example:
b = Bar()
b.iterations = 5
b.hello()
# "hello world" will be displayed 3 times
Is there a way to do this or is it anti pattern to python ?
I found a solution to your problem.
The idea is to write your own decorator.
Since that decorator is a function wrapping your method, it has access to the class instance using the *args first index. From there, you can access the iterations variable:
def decorator(iterations, source):
def inner_transition(func):
return func
return inner_transition
def custom_transition(source):
def inner_custom_transition(func):
def wrapper(*args, **kwargs):
iterations = args[0].iterations
return decorator(iterations=iterations, source=source)(func)(*args, **kwargs)
return wrapper
return inner_custom_transition
class Foo:
iterations = 3
class Bar(Foo):
#custom_transition(source="my_source")
def hello(self, string_to_show, additional_string = "default"):
print(string_to_show)
print(additional_string)
bar = Bar()
bar.hello("hello", additional_string="++++")
Result:
hello
++++

python-Decorator function inside Class

I have the below code which works fine if I remove self from methods
class tests:
def __init__(self):
pass
def func(self,a):
def wrapp(x):
y=x+2
return a(y)
return wrapp
#func
def func1(self,b):
return b
print (tests.func1(10))
I believe decorator function are functions that return another function. Will that not work inside class? Ignore the indentation error as I am not achievable when I paste the code here..
Please help me how I can achieve this scenario inside class..
You can just declare your decorator outside of the class. Also, when you are decorating a class method, it seems you need to pass the self variable from the wrapper to the decorated function (changed the names for more clarity):
def add_two(fn):
def wrapper(self, x):
y = x + 2
return fn(self, y)
return wrapper
class Test:
#add_two
def func1(self, b):
return b
f = Test()
f.func1(5) # returns 7
This issue here isn't the decorator at all. This issue is you are using func1 and your decorator as static methods without removing the self argument. If you remove the self arguments this will work fine.
Without staticmethod decorator
class Test:
def add_two(func=None):
def wrapper_add_two(*args, **kwargs):
return func(*args, **kwargs) + 2
return wrapper_add_two
#add_two
def func1(b):
return b
print(Test.func1(10)) #12
With staticmethod decorator
Unfortunately using them in this manner stores them as unbound static methods and you need to bind them for this to work properly.
class Test:
#staticmethod
def add_two(func):
def wrapper_add_two(*args, **kwargs):
return func.__func__(*args, **kwargs) + 2
return wrapper_add_two
#add_two.__func__
#staticmethod
def func1(b):
return b
print(Test.func1(10)) #12
Running with the staticmethod decorator and without the function binding gives you
TypeError: 'staticmethod' object is not callable

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 with inside myDecorator.__call__() not inside aFunction().
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.
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
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).

How to apply a python decorator to a function? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Understanding Python decorators
Could you please give a short code example that explains decorators?
def spam(func):
def wrapped(*args, **kwargs):
print "SPAM"
return func(*args, **kwargs)
return wrapped
#spam #this is the same as doing eggs = spam(eggs)
def eggs():
print "Eggs?"
Notice you can also use classes to write decorators
class Spam(object):
def __init__(self, func):
self.func = func
def __repr__(self):
return repr(self.func)
def __call__(self, *args, **kwargs):
print "SPAM"
return self.func(*args, **kwargs)
#Spam
def something():
pass
A decorator takes the function definition and creates a new function that executes this
function and transforms the result.
#deco
def do():
...
is equivalent to:
do = deco(do)
Example:
def deco(func):
def inner(letter):
return func(letter).upper() #upper
return inner # return a function object
#This
#deco
def do(number):
return chr(number) # number to letter
#end
# is equivalent to this
def do2(number):
return chr(number)
do2 = deco(do2)
#end
# 65 <=> 'a'
print(do(65))
print(do2(65))
>>> B
>>> B
To understand the decorator, it is important to notice, that decorator created a new function do which is inner that executes func and transforms the result.

Categories