Everyone who has used python , has sooner or later come across decorators . That's that thing sitting on a function which magically does something special . It looks like :
That @mydecorator is nothing but another function that's going to run along with the function it's been applied to . If we need to define a decorator function , we can say :
It is a function, that takes another function as an input argument, does some processing, and returns another function.
2. It takes one argument . That argument will be a function.
3. Inside the function , we define another function (name doesn't matter) and return that function argument itself (or with some processing like above) .
4. Finally , we return the inner function but, without parenthesis. So it doesn't execute.
Now when this function that we defined is applied to another, it of course gives you a square of a number. But what's happening behind the scene is.
1. An object of the inner_function type gets created .
f = square(number)
2. f
<function inner_func at 0x00000000032892E8>
3. f ()
25
Decorator with arguments
What if we want to pass arguments with the decorator itself. Let's see an example.
According to Python docs, Class instances are callable only when the class has a
@mydecorator
def my_function
Today we are going to learn , how to write decorators of our own . How this works:
That @mydecorator is nothing but another function that's going to run along with the function it's been applied to . If we need to define a decorator function , we can say :
It is a function, that takes another function as an input argument, does some processing, and returns another function.
#Example: A decorator for function with no arguments
def square(func):
def inner_func():
return func() ** 2
return inner_func
@square
def number():
return 5
Designing Steps of the above example:
1. We define a decorator function like a normal function. Example : square function (Refer above example).2. It takes one argument . That argument will be a function.
3. Inside the function , we define another function (name doesn't matter) and return that function argument itself (or with some processing like above) .
4. Finally , we return the inner function but, without parenthesis. So it doesn't execute.
Now when this function that we defined is applied to another, it of course gives you a square of a number. But what's happening behind the scene is.
1. An object of the inner_function type gets created .
f = square(number)
2. f
<function inner_func at 0x00000000032892E8>
3. f ()
25
Now what if we wanted to apply the decorator on a function that accepts an argument . Like our square function above.
#Example with function that takes arguments (In this case, a string) def reverse_string(func): def inner_func(*args, **kwargs): text1, text2 = func(*args, **kwargs) print 'Reversed String:' + text1[::-1] + text2[::-1] return inner_func
@reverse_string def get_string(s, s1): return s, s1
>>> get_string('Let"s' , 'decorate') Reversed String:s"teLetaroced
Decorator with arguments
What if we want to pass arguments with the decorator itself. Let's see an example.
def multiplyby(num): def middlefun(func): def inner_fun(*args, **kwargs): value = func(*args) return value * num return inner_fun return middlefun @multiplyby(10) def mynum(num): return num print mynum(5)
>>> 50
Let's build a cool time measuring decorator that can measure execution time of any function.
# A decorator to measure function time def measure_this(func): import time def inside_func(*args, **kwargs): t1 = time.time() my_func = func(*args, **kwargs) t2 = time.time() print 'It took {} seconds to run the function: {}'.format(t2 - t1, func.__name__) return inside_func @measure_this def lets_wait(t): import time time.sleep(t) return #Example usage: >>> lets_wait(1) It took 1.0 seconds to run the function: lets_wait >>> lets_wait(3) It took 3.0 seconds to run the function: lets_wait
We can design a decorator using classes too.
But before we get into that , I want to tell you something about a special method called
__call__
According to Python docs, Class instances are callable only when the class has a
__call__()
method . See the example below.>>> class Hello: def __init__(self, name): self.name = name def __call__(self): return 'Calling %s' % self.name >>> h = Hello('Arc') >>> h <__main__.Hello instance at 0x0000000003159508> >>> h() 'Calling Arc'
If we don't define that __call__ method, and we try to run the class instance, we get an error
>>> h()
Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
h()
AttributeError: Hello instance has no __call__ method
With this knowledge , we go ahead and design a class decorator
# Class based decorator class decorator_class(object): def __init__(self, func): self.original_func = func def __call__(self, *args, **kwargs): return 'Hi ' + self.original_func(*args, **kwargs) @decorator_class def hello(name): return name >>> hello('Batman') 'Hi Batman'
No comments:
Post a Comment