Search code examples
pythondjangopylonstornadobottle

Decorators vs. classes in python web development


I've noticed three main ways Python web frameworks deal request handing: decorators, controller classes with methods for individual requests, and request classes with methods for GET/POST.

I'm curious about the virtues of these three approaches. Are there major advantages or disadvantages to any of these approaches? To fix ideas, here are three examples.

Bottle uses decorators:

@route('/')
def index():
    return 'Hello World!'

Pylons uses controller classes:

class HelloController(BaseController):
    def index(self):
        return 'Hello World'

Tornado uses request handler classes with methods for types:

 class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

Which style is the best practice?


Solution

  • There's actually a reason for each of the three methods you listed, specific to each project.

    • Bottle tries to keep things as simple/straightforward as possible for the programmer. With decorators for routes you don't have to worry about the developer understanding OOP.
    • Pylons development goal is to make code re-usable and to be easily integrated with WSGI-style HTTP process routing. As such, they have chosen a very OOP way of organizing routes. As an example, you could copy & paste HelloController into any Pylons app and it should just magically work. Even if said app is being served up via WSGI in some complicated fashion.
    • Tornado has yet another reason for doing things the way it does: Tornado's epoll-based IOLoop (in conjunction with tornado.web.Application) instantiates each RequestHandler as requests come in. By keeping each RequestHandler limited to a specific GET or POST this allows IOLoop to quickly instantiate the class, process the request, and finally let it get garbage collected. This keeps it fast and efficient with a small memory footprint regardless of how many RequestHandlers your application has. This is also the reason why Tornado can handle so many more simultaneous requests than other Python-based web servers (each request gets its own instance).

    Now, having said all that you should know that you can always override the default framework behavior. For example, I wrote a MethodDispatcher for Tornado that makes it work more like Pylons (well, I had CherryPy in mind when I wrote it). It slows down Tornado a tiny amount (and increases the memory footprint slightly) due to having one large RequestHandler (as opposed to a lot of small ones) but it can reduce the amount of code in your app and make it a little easier to read (In my biased opinion, of course =).