Python Decorators
Python Decorators
Python Decorators
For example, you had a white car with basic wheel setup and a mechanic changes
the color of your car to red and fits alloy wheels to it then the mechanic decorated
your car, similarly a decorator in python is used to decorate(or add functionality or
feature) to your existing code.
In [20]:
1 # some function
2 def first(msg):
3 print(msg)
4
5 # second function
6 def second(func, msg):
7 func(msg)
8
9 # calling the second function with first as argument
10 second(first, "Hello!")
Hello!
While in the example above the function second took the function first as an argument
and used it, a function can also return a function.
When there is nested functions(function inside a function) and the outer function
returns the inner function it is known as Closure in python.
1 def dollar(fn):
2 def new(*args):
3 return '$' + str(fn(*args))
4 return new
5
6 @dollar
7 def price(amount, tax_rate):
8 return amount + amount*tax_rate
9
10 print(price(100,0.1))
11
12 @dollar
13 def hello(number):
14 return number*2
15
16 print(hello(30))
$110.0
$60
1 def heading(f):
2 def wrapped():
3 return '<H1>' + f() + '</H1>'
4 return wrapped
5
6 def bold(f):
7 def wrapped():
8 return '<b>' + f() + '</b>'
9 return wrapped
10
11 def italic(f):
12 def wrapped():
13 return '<i>' + f() + '</i>'
14 return wrapped
15
16
17 @heading
18 @bold
19 @italic
20 def welcome():
21 return 'Welcome to Decorator'
22
23 print(welcome())
<H1><b><i>Welcome to Decorator</i></b></H1>
Welcome to Decorator
1 # Using Decorators in Python
2
3 A decorator gives a function a new behavior without
changing the function itself. A decorator is used to add
functionality to a function or a class. In other words,
python decorators wrap another function and extends the
behavior of the wrapped function, without permanently
modifying it.
4
5 Now, Let's understand the decorators using an example:-
In [1]:
1 # a decorator function
2 def myDecor(func):
3 # inner function like in closures
4 def wrapper():
5 print("Modified function")
6 func()
7 return wrapper
8
9
10 def myfunc():
11 print('Hello!!')
12
13 # Calling myfunc()
14 myfunc()
15
16 # decorating the myfunc function
17 decorated_myfunc = myDecor(myfunc)
18
19 # calling the decorated version
20 decorated_myfunc()
Hello!!
Modified function
Hello!!
In the code example above, we have followed the closure approach but instead of
some variable, we are passing a function as argument, hence executing the function
with some more code statements.
We passed the function myfunc as argument to the function myDecor to get the
decorated version of the myfunc function.
Now rather than passing the function as argument to the decorator function, python
provides us with a simple way of doing this, using the @ symbol.
In [3]:
Modified function
Hello!!
In the code example above, @myDecor is used to attach the myDecor() decorator to
any function you want.
So when we will call myfunc(), instead of execution of the actual body of myfunc()
function, it will be passed as an argument to myDecor() and the modified version of
myfunc() is returned which will be executed.
So, basically @ is used to attach any decorator with name Decorator_name to any
function in python programming language.
For this, we are going to use args and *kwargs as the arguments in the inner function
of the decorator.
The *args in function definition is used to pass a variable number of arguments to any
function. It is used to pass a non-keyworded, variable-length argument list.
1 def myDecor(func):
2 def wrapper(*args, **kwargs):
3 print('Modified function')
4 func(*args, **kwargs)
5 return wrapper
6
7 @myDecor
8 def myfunc(msg):
9 print(msg)
10
11 # calling myfunc()
12 myfunc('Hey')
13
14
Modified function
Hey
In the example, the myfunc() function is taking an argument msg which is a message
that will be printed.
The call will result in the decorating of function by myDecor decorator and argument
passed to it will be as a result passed to the args of wrapper() function which will
again pass those arguments while calling myfunc() function.
And finally, the message passed will be printed after the statement 'Modified
function'.
1 # first decorator
2 def star(f):
3 def wrapped():
4 return '**' + f() + '**'
5 return wrapped
6
7 # second decorator
8 def plus(f):
9 def wrapped():
10 return '++' + f() + '++'
11 return wrapped
12
13 @star
14 @plus
15 def hello():
16 return 'hello'
17
18 print(hello())
**++hello++**
In [ ]:
1 class MyClass:
2 def method(self):
3 return 'instance method called', self
4
5 @classmethod
6 def classmethod(cls):
7 return 'class method called', cls
8
9 @staticmethod
10 def staticmethod():
11 return 'static method called'
1 import time
2
3 def timing(f):
4 def wrapper(*args, **kwargs):
5 start = time.time()
6 result = f(*args,**kwargs)
7 end = time.time()
8 print(f.__name__ +" took " + str((end-start)*1000) +
9 return result
10 return wrapper
11
12 @timing
13 def calcSquare(numbers):
14 result = []
15 for number in numbers:
16 result.append(number*number)
17 return result
18
19 @timing
20 def calcCube(numbers):
21 result = []
22 for number in numbers:
23 result.append(number*number*number)
24 return result
25
26 # main method
27 if __name__ == '__main__':
28 array = range(1,100000)
29 sq = calcSquare(array)
30 cube = calcCube(array)
Here we have used the time module and the time before starting a function to start
variable and the time after a function ends to end variable. f.name gives the name of
the current function that is being decorated. The code range(1,100000) returned a list
of numbers from 1 to 100000.
So, by using decorators, we avoided using the same code in both the functions
separately (to get the time of execution). This helped us in maintaining a clean code
as well as reduced the work overhead.
In [11]:
1 # PythonDecorators/my_decorator.py
2 class my_decorator(object):
3
4 def __init__(self, f):
5 print("inside my_decorator.__init__()")
6 f() # Prove that function definition has completed
7
8 def __call__(self):
9 print("inside my_decorator.__call__()")
10
11 @my_decorator
12 def aFunction():
13 print("inside aFunction()")
14
15 print("Finished decorating aFunction()")
16
17 aFunction()
inside my_decorator.__init__()
inside aFunction()
Finished decorating aFunction()
inside my_decorator.__call__()
In [8]:
1 # PythonDecorators/decorator_with_arguments.py
2 class decorator_with_arguments(object):
3
4 def __init__(self, arg1, arg2, arg3):
5 """
6 If there are decorator arguments, the function
7 to be decorated is not passed to the constructor!
8 """
9 print("Inside __init__()")
10 self.arg1 = arg1
11 self.arg2 = arg2
12 self.arg3 = arg3
13
14 def __call__(self, f):
15 """
16 If there are decorator arguments, __call__() is only c
17 once, as part of the decoration process! You can only
18 it a single argument, which is the function object.
19 """
20 print("Inside __call__()")
21 def wrapped_f(*args):
22 print("Inside wrapped_f()")
23 print("Decorator arguments:", self.arg1, self.arg2
24 f(*args)
25 print("After f(*args)")
26 return wrapped_f
27
28 @decorator_with_arguments("hello", "world", 42)
29 def sayHello(a1, a2, a3, a4):
30 print('sayHello arguments:', a1, a2, a3, a4)
31
32 print("After decoration")
33
34 print("Preparing to call sayHello()")
35 sayHello("say", "hello", "argument", "list")
36 print("after first sayHello() call")
37 sayHello("a", "different", "set of", "arguments")
38 print("after second sayHello() call")
Inside __init__()
Inside __call__()
After decoration
Preparing to call sayHello()
Inside wrapped_f()
1 # PythonDecorators/entry_exit_class.py
2 class entry_exit(object):
3
4 def __init__(self, f):
5 self.f = f
6
7 def __call__(self):
8 print("Entering", self.f.__name__)
9 self.f()
10 print("Exited", self.f.__name__)
11
12 @entry_exit
13 def func1():
14 print("inside func1()")
15
16 @entry_exit
17 def func2():
18 print("inside func2()")
19
20 func1()
21 func2()
Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2
In [9]:
1 # PythonDecorators/entry_exit_function.py
2 def entry_exit(f):
3 def new_f():
4 print("Entering", f.__name__)
5 f()
6 print("Exited", f.__name__)
7 return new_f
8
9 @entry_exit
10 def func1():
11 print("inside func1()")
12
13 @entry_exit
14 def func2():
15 print("inside func2()")
16
17 func1()
18 func2()
19 print(func1.__name__)
Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2
new_f
In [7]:
1 # PythonDecorators/decorator_function_with_arguments.py
2 def decorator_function_with_arguments(arg1, arg2, arg3):
3 def wrap(f):
4 print("Inside wrap()")
5 def wrapped_f(*args):
6 print("Inside wrapped_f()")
7 print("Decorator arguments:", arg1, arg2, arg3)
8 f(*args)
9 print("After f(*args)")
10 return wrapped_f
11 return wrap
12
13 @decorator_function_with_arguments("hello", "world", 42)
14 def sayHello(a1, a2, a3, a4):
15 print('sayHello arguments:', a1, a2, a3, a4)
16
17 print("After decoration")
18
19 print("Preparing to call sayHello()")
20 sayHello("say", "hello", "argument", "list")
21 print("after first sayHello() call")
22 sayHello("a", "different", "set of", "arguments")
23 print("after second sayHello() call")
Inside wrap()
After decoration
Preparing to call sayHello()
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: say hello argument list
After f(*args)
after first sayHello() call
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: a different set of arguments
After f(*args)
after second sayHello() call
In [ ]:
1 #/Users/SurendraMac/Python27
In [ ]:
1 # %load trace.py
2 #trace.py
3 def trace( aFunc ):
4 """Trace entry, exit and exceptions."""
5 def loggedFunc(*args, **kw ):
6 print("enter", aFunc.__name__)
7 try:
8 result= aFunc(*args,**kw )
9 except Exception, e:
10 print("exception",aFunc.__name__, e)
11 raise
12 print("exit", aFunc.__name__)
13 return result
14 loggedFunc.__name__= aFunc.__name__
15 loggedFunc.__doc__= aFunc.__doc__
In [ ]:
1 # %load trace_client.py
2 ##Here's a class which uses our @trace decorator.
3 ##trace_client.py
4 from trace1 import trace
5 class MyClass(object):
6 #@trace
7 def __init__( self, someValue,name='Surendra' ):
8 """Create a MyClass instance."""
9 self.value= someValue
10 self.name=name
11 print "Name and Value ",self.name,self.value
12 #@trace
13 def doSomething( self, anotherValue,age=35):
14 """Update a value."""
15 self.value += anotherValue
16 print "another Value ",self.value
17
18
19 def hello1():
20 def hello(*arg):
21 '''This is hello document'''
22 print "Hi"
23 print "Hello1"
24 return hello()
25
26 hello1()
27
28 #mc=MyClass(23)
29 #mc.doSomething(60)
30 #m=MyClass(45)
31 #enter __init__
32 #exit __init__
33 #mc.doSomething( 15 )
34 #m.doSomething(54)
35 #enter doSomething
36 #exit doSomething
37 # print mc.value
38 #38
39
In [ ]:
1 # %load trace2.py
2 #trace.py
3 def trace(a):
4 """Trace entry, exit and exceptions."""
5 def hello(*b):
6 return b
7 return hello
8
9
10 print trace(78)
11
In [ ]:
1 # %load trace_client3.py
2 ##Here's a class which uses our @trace decorator.
3 ##trace_client.py
4 from trace1 import trace
5 class MyClass:
6 #@trace
7 ## def __init__( self, someValue):
8 ## """Create a MyClass instance."""
9 ## self.value= someValue
10 ## print "Value ",self.value
11 #@trace
12 ## def doSomething( self, anotherValue):
13 ## """Update a value."""
14 ## self.value += anotherValue
15 ## print "another Value ",self.value
16 @trace
17 def hello(self):
18 pass
19
20
21 mc=MyClass()
22 #mc.doSomething(60)
23 #m=MyClass(45)
24 #enter __init__
25 #exit __init__
26 #mc.doSomething( 15 )
27 #m.doSomething(54)
28 #enter doSomething
29 #exit doSomething
30 # print mc.value
31 #38
32
In [ ]:
1 # %load trace1.py
2 #trace.py
3 def trace( aFunc ):
4 """Trace entry, exit and exceptions."""
5 def loggedFunc(*args,**kwarg):
6 print "enter", aFunc.__name__
7 print "Document of Function", aFunc.__doc__
8 try:
9 result= aFunc(*args,**kwarg)
10 except Exception, e:
11 print "exception",aFunc.__name__, e
12 raise
13 print "exit", aFunc.__name__
14 #print result
15 return result
16 #loggedFunc.__name__= aFunc.__name__
17 #loggedFunc.__doc__= aFunc.__doc__
18 #print "Returning Log "
19 return loggedFunc
20
21
22 @trace
23 def add(x,y):
24 '''This is addition of Two Variable'''
25 return x+y
26
27
28 @trace
29 def sub(x,y):
30 ''' This is Substraction '''
31 return x-y
32
33
34 print add(5,8)
35 print sub(9,3)
36
37
1 Class Methods
2
3 Let’s compare that to the second method,
MyClass.classmethod. I marked this method with a
@classmethod decorator to flag it as a class method.
4
5 Instead of accepting a self parameter, class methods take a
cls parameter that points to the class—and not the object
instance—when the method is called.
6
7 Because the class method only has access to this cls
argument, it can’t modify object instance state. That would
require access to self. However, class methods can still
modify class state that applies across all instances of the
class.
8
9 Static Methods
10 The third method, MyClass.staticmethod was marked with a
@staticmethod decorator to flag it as a static method.
11
12 This type of method takes neither a self nor a cls
parameter (but of course it’s free to accept an arbitrary
number of other parameters).
13
14 Therefore a static method can neither modify object state
nor class state. Static methods are restricted in what data
they can access - and they’re primarily a way to namespace
your methods.
15
16 Let’s See Them In Action!
17 I know this discussion has been fairly theoretical up to
this point. And I believe it’s important that you develop
an intuitive understanding for how these method types
differ in practice. We’ll go over some concrete examples
now.
18
19 Let’s take a look at how these methods behave in action
when we call them. We’ll start by creating an instance of
the class and then calling the three different methods on
it.
20
21 MyClass was set up in such a way that each method’s
implementation returns a tuple containing information for
us to trace what’s going on — and which parts of the class
or object the method can access.
22
23 Here’s what happens when we call an instance method:
24
25 >>> obj = MyClass()
26 >>> obj.method()
27 ('instance method called', <MyClass instance at
0x101a2f4c8>)
28 This confirmed that method (the instance method) has access
to the object instance (printed as <MyClass instance>) via
the self argument.
29
30 When the method is called, Python replaces the self
argument with the instance object, obj. We could ignore the
syntactic sugar of the dot-call syntax (obj.method()) and
pass the instance object manually to get the same result:
31
32 >>> MyClass.method(obj)
33 ('instance method called', <MyClass instance at
0x101a2f4c8>)
34 Can you guess what would happen if you tried to call the
method without first creating an instance?
35
36 By the way, instance methods can also access the class
itself through the self.__class__ attribute. This makes
instance methods powerful in terms of access restrictions -
they can modify state on the object instance and on the
class itself.
37
38 Let’s try out the class method next:
39
40 >>> obj.classmethod()
41 ('class method called', <class MyClass at 0x101a2f4c8>)
42 Calling classmethod() showed us it doesn’t have access to
the <MyClass instance> object, but only to the <class
MyClass> object, representing the class itself (everything
in Python is an object, even classes themselves).
43
44 Notice how Python automatically passes the class as the
first argument to the function when we call
MyClass.classmethod(). Calling a method in Python through
the dot syntax triggers this behavior. The self parameter
on instance methods works the same way.
45
46 Please note that naming these parameters self and cls is
just a convention. You could just as easily name them
the_object and the_class and get the same result. All that
matters is that they’re positioned first in the parameter
list for the method.
47
48 Time to call the static method now:
49
50 >>> obj.staticmethod()
51 'static method called'
52 Did you see how we called staticmethod() on the object and
were able to do so successfully? Some developers are
surprised when they learn that it’s possible to call a
static method on an object instance.
53
54 Behind the scenes Python simply enforces the access
restrictions by not passing in the self or the cls argument
when a static method gets called using the dot syntax.
55
56 This confirms that static methods can neither access the
object instance state nor the class state. They work like
regular functions but belong to the class’s (and every
instance’s) namespace.
57
58 Now, let’s take a look at what happens when we attempt to
call these methods on the class itself - without creating
an object instance beforehand:
59
60 >>> MyClass.classmethod()
61 ('class method called', <class MyClass at 0x101a2f4c8>)
62
63 >>> MyClass.staticmethod()
64 'static method called'
65
66 >>> MyClass.method()
67 TypeError: unbound method method() must
68 be called with MyClass instance as first
69 argument (got nothing instead)
class C(object): @classmethod def fun(cls, arg1, arg2, ...): .... fun: function that needs
to be converted into a class method returns: a class method for function. A class
method is a method which is bound to the class and not the object of the class. They
have the access to the state of the class as it takes a class parameter that points to
the class and not the object instance. It can modify a class state that would apply
across all the instances of the class. For example it can modify a class variable that
will be applicable to all the instances. Static Method
A class method takes cls as first parameter while a static method needs no specific
parameters. A class method can access or modify class state while a static method
can’t access or modify it. In general, static methods know nothing about class state.
They are utility type methods that take some parameters and work upon those
parameters. On the other hand class methods must have class as parameter. We use
@classmethod decorator in python to create a class method and we use
@staticmethod decorator to create a static method in python. When to use what?
We generally use class method to create factory methods. Factory methods return
class object ( similar to a constructor ) for different use cases. We generally use static
methods to create utility functions. How to define a class method and a static
method?
As explained above we use static methods to create utility functions. In the below
example we use a static method to check if a person is adult or not.
Implementation
brightness_4
()
{}
(5, 7)
{}
('hello', 'hi', 23, 34)
{}
(2, 3, 4)
{'var1': 20, 'var2': 40}
In [ ]: