*New* Jython Basics - Decorators

Submitted By: Josh Juneau

Must use Jython 2.5a1+ to run the examples contained within this article

Introduction

A new era in Jython development has arrived. We Jython developers will soon be able to begin harnessing many of the powerful features of the current Python language distribution. Using the latest alpha release of Jython (2.5a+), we can take a look at these new features today. Python is a language whose popularity has grown tremendously over the past several years, partially due to the great features that have been added. One of the newer features are known as decorators, which may appear daunting at first due to their odd syntax. As many Jython developers are also Java gurus, the syntax may look strikingly similar to the JavaEE Annotation. However, the Jython decorator is significantly different yet equally as powerful.

What Is A Decorator?

You can do a Google search on Python decorators and find about a thousand articles, blogs, and tutorials on the subject. One of the most useful references I found for decorator discussion came from an article written by Michele Simionato (reference below). In the article, Michele explains that decorators are useful for helping to reduce boilerplate code and redundancy, and they also help to make code more organized and readable.

Decorator: A function that transforms another function. A function can be passed into a decorator, modified, and then returned. They can be useful for reducing code duplication, wrapping functionality, etc.

Example 1

The first example was taken from this article about decorators. It is probably the most straight forward example that I have found for decorators, and the one that helped me to understand them the best. Quick and to the point, this example shows the raw structure of what a basic decorator can accomplish.

def decorator_function(target):
    # Do something with the target function
    target.attribute = 1
    return target

def target(a,b):
    return a + b

# This is what the decorator actually does
target = decorator_function(target)

The following code has the same functionality, but it uses decorators. Basically, with the use of decorators you can omit the last line of the previous example.

def decorator_function(target):
    # Do something with the target function
    target.attribute = 1
    return target

#Here is the decorator with the syntax '@function_name'
@decorator_function
def target(a,b):
    return a + b

Both of the examples have the following result:

>>> target(1,3)
3
>>> target.attribute
1

Example 2

A second, more useful example shows how we can use a decorator to validate input on any Jython function. For instance, in the example below we want to ensure that our function is only accepting numeric input. We define a function which we will use to validate or data and return a boolean value. The decorator function will be used to evaluate whether or not the input to our function is valid by using the boolean value which it receives. We can pass the validation function into the decorator function as input in order to make this process occur. Take a look at the code below as I think it speaks for itself.

# Return true if the value is numeric, false if it is not.
def is_number(x):
    for value in x:
        if type(value) == type(1):
            print type(value)
            return True
        else:
            print type(value)
            return False
    
# Apply to a function to determine whether data passed into it is valid
def require(expr):
    def decorator(func):
        def wrapper(*__args,**__kw):
            assert eval(expr),"Precondition failed...please pass valid input"
            return func(*__args,**__kw)
        return wrapper
    return decorator

# Decorate our test function to have our require function applied to it.  We
# could pass any boolean value into require in order to check for compliance
# at runtime.
@require("is_number(__args)")
def test(*args):
    print args[0]
test(363)
test("Hello world")

The first call to our test function will return the value that we passed into it. This is obviously not a useful feature, but in a real-world application we would do something with that value once it was validated. The second call to our test function will fail because the value passed into it is not numeric. As you can see, the require decorator is passed the is_number(args) function and evaluated at runtime...thus, giving us our validation. Quite useful indeed!

Example 3

Lastly, another more complex example...the classic "Prime Number" scenario. If nothing else, please use this example as a reference for exactly how powerful decorators can be.

# time a function using time.time() and the a @ function decorator
# tested with Jython 2.5a1+  

import time

def print_timing(func):
    def wrapper(*arg):
        t1 = time.time()
        res = func(*arg)
        t2 = time.time()
        print res
        print '%s took %0.3f ms' % (func.func_name, (t2-t1)*1000.0)
        return res
    return wrapper

# declare the @ decorator just before the function, invokes print_timing()
@print_timing
def getPrimeList(n):
    """ returns a list of prime numbers from 2 to < n using a sieve algorithm"""
    if n < 2:  return []
    if n == 2: return [2]
    # do only odd numbers starting at 3
    s = range(3, n+1, 2)
    # n**0.5 may be slightly faster than math.sqrt(n)
    mroot = n ** 0.5
    half = len(s)
    i = 0
    m = 3
    while m <= mroot:
        if s[i]:
            j = (m*m-3)//2
            s[j] = 0
            while j < half:
                s[j] = 0
                j += m
        i = i+1
        m = 2*i+3
    return [2]+[x for x in s if x]

if __name__ == "__main__":
    print "prime numbers from 2 to <10,000,000 using a sieve algorithm"
    primeList = getPrimeList(1000)
    time.sleep(2.5)

This example has been slightly modified from the discussion found at http://www.daniweb.com/code/snippet368.html because of two reasons. First, I wanted to print out the prime number listing so that you could see how this function was called and modified by the decorator a bit more clearly. Second, the referenced discussion uses the number 10,000,000 and there are just too many primes to list out in that large of a number!

Conclusion

Decorators are bound to become a major benefit to Jython applications. By reviewing the brief examples which I have posted above, one can see how useful the decorator concept may be. Certainly this type of addition to the language is not something that will be used in every piece of Jython code, but it can be enormously beneficial in the places where it is used properly. One can only imagine the possibilities of including decorators with the many Java APIs which are available to the Jython community.

References

Timing a Function (Python)

Python Decorators Don't Have to be (that) Scary

The decorator module