## Demonstration of Decorators:

``````(Adapted from Jon Jacky's Intro to Python class)

``````

### Creating a function in a function....

``````

In :

return i + n

``````

NOTE: you oculd use lambda for something as simple as this...

``````

In :

``````
``````

In :

``````
``````

Out:

3

``````
``````

In :

``````
``````

In :

``````
``````

Out:

4

``````

A function that takes a function as an argument, and returns a function can be a decorator.

It usually creates a function inside its scope...

Pass a function as an argument, use that to define the function you return.

(first a couple functions to use...)

``````

In :

def odd(i):
return i%2
def even(i):
return not odd(i)

``````

And write a wrapper for them....

``````

In :

def sieve(f):
def siever(s):
return [x for x in s if f(x)]
return siever

``````

Make a couple of sieves:

``````

In :

oddsieve = sieve(odd)
evensieve = sieve(even)

``````

And try them out:

``````

In :

s = range(10)
s

``````
``````

Out:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

``````
``````

In :

oddsieve(s)

``````
``````

Out:

[1, 3, 5, 7, 9]

``````
``````

In :

evensieve(s)

``````
``````

Out:

[0, 2, 4, 6, 8]

``````

The decorator operator @ abbreviates the preceding pattern

```@f def g``` means

`g = f(g)`

``````

In :

@sieve
def osieve(i):
return i % 2

@sieve
def esieve(i):
return not (i % 2)

``````
``````

In :

osieve(s)

``````
``````

Out:

[1, 3, 5, 7, 9]

``````
``````

In :

esieve(s)

``````
``````

Out:

[0, 2, 4, 6, 8]

``````

A callable class can be used as a function, so you can also use a class as a decorator

(classes and objects are callable (via `__init__` and `__call__`))

``````

In :

class Memoize:
"""
memoize decorator from avinash.vora
http://avinashv.net/2008/04/python-decorators-syntactic-sugar/
"""
def __init__(self, function):  # runs when memoize class is called
self.function = function
self.memoized = {}

def __call__(self, *args):  # runs when memoize instance is called
try:
return self.memoized[args]
except KeyError:
self.memoized[args] = self.function(*args)
return self.memoized[args]

``````

To use it -- the nifty decorator syntax:

``````

In :

@Memoize        # same effect as sum2x = memoize(sum2x)
def sum2x(n):
return sum(2 * i for i in xrange(n))  # takes time when n > 10 million

``````

call it:

``````

In :

sum2x(10)

``````
``````

Out:

90

``````
``````

In :

sum2x(10)

``````
``````

Out:

90

``````

But slow if you call it with a big number:

``````

In :

import time
start = time.clock()
sum2x(10000000)
print "it took %f seconds to run"%(time.clock() - start)

``````
``````

it took 1.580831 seconds to run

``````

But the second time...

``````

In :

import time
start = time.clock()
sum2x(10000000)
print "it took %f seconds to run"%(time.clock() - start)

``````
``````

it took 0.000192 seconds to run

``````

Quiz time: what type of object is sum2x ?

``````

In :

repr(sum2x)

``````
``````

Out:

'<__main__.Memoize instance at 0x103708170>'

``````
``````

In [ ]:

``````