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)
Related
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
++++
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
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
I am decorating a function foo with a_decorator
#a_decorator(params)
def foo(x):
# print('Called',decorator_name)
# other magic
Is there a way to access the name a_decorator inside foo so that I can print
'Called a_decorator'
def a_decorator(some_function):
def wrapper():
some_function()
return some_val
return wrapper
It can be done by attaching decorator name to wrapper function:
from functools import wraps
def a_decorator(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
val = fn(*args, **kwargs)
# some action
return val
wrapper.decorated_by = a_decorator
return wrapper
#a_decorator
def foo(x):
print(x, foo.decorated_by.__name__)
foo('test') # prints: test a_decorator
Function in python are first class and you can treat them as an object, attach attributes, etc.
I am writing a bunch of code that has a possibility of mutable outputs, like an arithmetic function where I could have the output be a float or an int. Basically my problem is that if I were to create a decorator for each object type I need (probably seven or eight), I would go insane with the constant repetition of:
def Int(fn):
def wrapper():
return int(fn())
return wrapper
What I want to have is a class like below that would create a decorator based on the name it's instantiated with and it would be a copy of the function above but with the appropriate type modifications.
class Decorator(object):
def __init__(self):
...
...
Int = Decorator()
# Then I can use #Int
Any help would be really appreciated. Thanks.
You cannot have Decorator know what name it will be assigned to. Assignment occurs after instantiation, so the object will have already been created by the time it is assigned a name.
You could however make a decorator that creates decorators dynamically:
from functools import wraps
def set_return_type(typeobj):
def decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
return typeobj(func(*args, **kwargs))
return wrapper
return decorator
You would then use this decorator by giving a type object argument for the type you want:
#set_return_type(int) # Causes decorated function to return ints
#set_return_type(float) # Causes decorated function to return floats
Below is a demonstration:
>>> from functools import wraps
>>> def set_return_type(typeobj):
... def decorator(func):
... #wraps(func)
... def wrapper(*args, **kwargs):
... return typeobj(func(*args, **kwargs))
... return wrapper
... return decorator
...
>>> #set_return_type(float)
... def test():
... return 1
...
>>> test()
1.0
>>>