Python Decorators

If you are beginner to this, please read them in the following links.
http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/
Bruce Eckel’s articles:
http://www.artima.com/weblogs/viewpost.jsp?thread=240808
http://www.artima.com/weblogs/viewpost.jsp?thread=240845

This blog just tries to find the difference between creating decorators using functions and classes.

Lets say that we have a class CLASS1. The object to which can be created like obj1 = CLASS1(). What if we try to call the object itself as in obj1()? This will call the magic method __call__(self). Let us see this in the python interpreter.

$ python
Python 2.7.5+ (default, Feb 27 2014, 19:39:55) 
[GCC 4.8.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class CLASS1(object):
...     def __init__(self):
...             print "Object initialization"
...     def __call__(self):
...             print "__call__() method"
... 
>>> obj1 = CLASS1()
Object initialization
>>> obj1()
__call__() method
>>> 

Now, with this understanding, we can easily compare and contrast the decorators created by functions and classes as shown below. The argument to the decorators can be manipulated at different levels and in the snippet below, they are identified by the print statements.

def fFunProcessor(fun):
    def __funWrapper(*args, **kwargs):
        print fun.__name__, "starts"
        val = fun(*args, **kwargs)
        print fun.__name__, "ends"
        return val
    return __funWrapper

def fDecoArgProcessor(*decoargs, **decokwargs):
    print "<< 1 >>", decoargs
    def __funProcessor(fun):
        print "<< 2 >>", decokwargs
        def __funWrapper(*args, **kwargs):
            print "<< 3 >>", decoargs, decokwargs
            print fun.__name__, "starts"
            val = fun(*args, **kwargs)
            print fun.__name__, "ends"
            return val
        return __funWrapper
    return __funProcessor

class cFunProcessor(object):
    def __init__(self, fun):
        self.fun = fun
        return
    def __call__(self, *args, **kwargs):
        print self.fun.__name__, "starts"
        val = self.fun(*args, **kwargs)
        print self.fun.__name__, "ends"
        return val

class cDecoArgProcessor(object):
    def __init__(self, *decoargs, **decokwargs):
        self.decoargs = decoargs
        self.decokwargs = decokwargs
        print ">> 1 <<", self.decoargs
        return
    def __call__(self, fun):
        print ">> 2 <<", self.decokwargs
        def __funWrapper(*args, **kwargs):
            print ">> 3 <<", self.decoargs, self.decokwargs
            print fun.__name__, "starts"
            val = fun(*args, **kwargs)
            print fun.__name__, "ends"
            return val
        return __funWrapper

@fFunProcessor
def fPrintLower(s):
    print s.lower()

@fDecoArgProcessor(1, 2, 3, foo = 'bar')
def fPrintUpper(s):
    print s.upper()

@cFunProcessor
def cPrintLower(s):
    print s.lower()

@cDecoArgProcessor(1, 2, 3, foo = 'bar')
def cPrintUpper(s):
    print s.upper()

fPrintLower('Hello World!')
fPrintUpper('Hello World!')
cPrintLower('Hello World!')
cPrintUpper('Hello World!')

From the name of the functions/classes itself, we can understand their purpose like what the function is expecting as input and what it can process. The wrapper function is required to process any arguments to the decorated function. On concluding, I do not see much difference and its just a matter of style or preference.

Happy Programming!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s