Tuesday 3 March 2015

My Python Notes: Decorators

Python is a hybrid programming language it support both object oriented programming as well as functional programming.

Like everything else in python function are also objects.

Functions and nested functions
















In the above example dir() is used to display the objects.
                   
Lets Look at a simple function
                                                basicFunction1.py

Lets run it
Since function are object they can be assigned to variables.
  
                                       basicFunction2.py

In basicFunction2.py 
At step #1 we assign the variable fun with the function label funIn.

step #2 we call function funIn by adding () -- parentheses to the variable fun.

Now let  us consider a function within a function ie.. nested functions.


Moving on to a function which returns a function.

                                              nestedBasics1.py
The the above code has two function funOut()-- outer function & funIn() -- inner function.

At step #1 the funOut() return label of the inner function label

step #2 the variable funCall has the inner function label.

step #3 now the inner function can be called by just adding () -- parentheses to the funCall variable.

Lets us now consider a variation of  nestedBasics1.py 


                                     nestedBasics2.py[closures]


In the above code 

At step #1 we declare variable name.Function funIn() store this value.

 step #2 the funOut() return label of the inner function label


step #3 the variable funCall has the inner function label.

step #4 now the inner function can be called by just adding () -- parentheses to the funCall variable.
When the inner function is being called it is able to display the value of name, since funIn() was able to store name value at Runtime (as mentioned in step #1).


Now lets us make nestedBasics2.py more dynamic 


Advantage of Nested functions

Lets us examine nestedBasics2.py 
At step #1 we are creating a new object of the function funOut()

step #2 we are creating another object of the function funOut()

Now lets us consider the class version of nestedBasics2.py.

                                    funClass.py

In the above code funClass.py,
At step #1  we need define an initializer  method __init__() 
step #2 we need a regular  method to act on the class attribute name.
step #3 we are creating a object of class funOut
step #4 now in order to access regular method we are using .dot operator

If we are writing a class with one regular method, it better to used nested functions.

Function which takes another function


In the above code we have two functions avg() & cal()
cal() --takes a function as one of the input parameters.

*args , **kwargs

A function can have positional arguments which are also know as named and mandatory, the second type of arguments are default which are optional and are also know as keyword arguments.

Positional arguments

 
In the above code , the function man() take two argument name & age ie positional arguments..
At step #1 we interchange two argument but get the same results(named argument)
step #2 we pass one argument and we get an error message(mandatory argument)

Default(Keyword) arguments

In the above code the function we have has two default arguments
At step #3 when we don't pass any argument the function consider the default values

Output 

*args--takes an unlimited number of positional arguments.It prevent the program from crashing,and is used when we don't know the number of input arguments.


*kwargs-- takes an unlimited number of keyword arguments, and store the data in a dictionary.

*args and *kwargs


Decorators 

Function without arguments.

Till now we have seen that function are object and few example on nested functions.
Lets us now move on to decorators.

                                          decoratorsbasic1.py


In the above example decoratorsbasic1.py we have two function at runtime ie  wrapper() & name()

wrapper() does three things.
1) It take a function as parameter(myFunc)
2)wrapper has an inner function which calls the passed function(myFunc), and store it in variable(y).
3)Finally wrapper return the inner function label(innerFunc)

At step #1 in decoratorsbasic1.py 
The innerFunc label is assigned to name and this in turn when executed
 changes the return value of the function name().

A slight modification to decoratorsbasic1.py  would be done by adding decorator symbol(@)
with the wrapper function label just above the name function.



Lets us consider another example

Function with arguments


In the above code we have are applying a decorators which checks ,that both the input parameter are non-zero and +ve.

Now lets us consider a decorator which can handle any number of positional & keyword arguments.

Let us write a decorators which can only read data passed to the function,but not executed the decorated function itself.

There are two function at runtime ie wrapper() , man().

wrapper does two things
 At step #1 the inner function is designed to handle any function signature.
step #2 innerWrap is able to access the argument of the decorated function man()
but it does not executed the contents of man() function.


Now lets access the parameter passed to the function as well execute the function itself.



At step #1 we are able to access the contents of passed arguments.
step #2 two things happen one, is that we execute the function to be wrapped man()
and the other is that we modify the return value of wrapped function man().

CLASS-DECORATOR

Till Now we have seen decorators which are built using function ,lets try using CLASSES






























In the above code foo1() & foo2() are passed as object to class myDecorator.
Now in myDecorator we have defined a __call__() method which gets called each time,
the decorated function gets called.




















addOne class adds one to the return value of the decorated function foo()