# Python Programming/Decorators

Duplicated code is recognized as bad practice in software for lots of
reasons, not least of which is that it requires more work to maintain.
If you have the same algorithm operating twice on different pieces of
data you can put the algorithm in a function and pass in the data to
avoid having to duplicate the code. However, sometimes you find cases
where the code itself changes, but two or more places still have
significant chunks of duplicated boilerplate code. A typical example
might be logging:

``` python
def multiply(a, b):
    result = a * b
    log("multiply has been called")
    return result

def add(a, b):
    result = a + b
    log("add has been called")
    return result
```

In a case like this, it\'s not obvious how to factor out the
duplication. We can follow our earlier pattern of moving the common code
to a function, but calling the function with different data is not
enough to produce the different behavior we want (add or multiply).
Instead, we have to pass a *function* to the common function. This
involves a function that operates on a function, known as a
*higher-order function*.

Decorator in Python is a syntax sugar for high-level function.

Minimal example of property decorator:

``` python
>>> class Foo(object):
...     @property
...     def bar(self):
...         return 'baz'
...
>>> F = Foo()
>>> print(F.bar)
baz
```

The above example is really just a syntax sugar for codes like this:

``` python
>>> class Foo(object):
...     def bar(self):
...         return 'baz'
...     bar = property(bar)
...
>>> F = Foo()
>>> print(F.bar)
baz
```

Minimal Example of generic decorator:

``` python
>>> def decorator(f):
...     def called(*args, **kargs):
...         print('A function is called somewhere')
...         return f(*args, **kargs)
...     return called
...
>>> class Foo(object):
...     @decorator
...     def bar(self):
...         return 'baz'
...
>>> F = Foo()
>>> print(F.bar())
A function is called somewhere
baz
```

A good use for the decorators is to allow you to refactor your code so
that common features can be moved into decorators. Consider for example,
that you would like to trace all calls to some functions and print out
the values of all the parameters of the functions for each invocation.
Now you can implement this in a decorator as follows:

``` python
#define the Trace class that will be 
#invoked using decorators
class Trace(object):
    def __init__(self, f):
        self.f =f

    def __call__(self, *args, **kwargs):
        print("entering function " + self.f.__name__)
        i=0
        for arg in args:
            print("arg {0}: {1}".format(i, arg))
            i =i+1
            
        return self.f(*args, **kwargs)
```

Then you can use the decorator on any function that you defined by:

``` python

@Trace
def sum(a, b):
    print "inside sum"
    return a + b
```

On running this code you would see output like

``` python
>>> sum(3,2)
entering function sum
arg 0: 3
arg 1: 2
inside sum
```

Alternately, instead of creating the decorator as a class, you could
have used a function as well.

``` python
def Trace(f):
    def my_f(*args, **kwargs):
        print("entering " +  f.__name__)
        result= f(*args, **kwargs)
        print("exiting " +  f.__name__)
        return result
    my_f.__name = f.__name__
    my_f.__doc__ = f.__doc__
    return my_f

#An example of the trace decorator
@Trace
def sum(a, b):
    print("inside sum")
    return a + b

#if you run this you should see
>>> sum(3,2)
entering sum
inside sum
exiting sum
5
```

Remember it is good practice to return the function or a sensible
decorated replacement for the function so that decorators can be
chained.

