views:

473

answers:

4

I recently started experimenting with Python decorators (and higher-order functions) because it looked like they might make my Django unit tests more concise. e.g., instead of writing:

def visit1():
    login()
    do_stuff()
    logout()

I could instead do

@handle_login
def visit1():
    do_stuff()

However, after some experimenting, I have found that decorators are not as simple as I had hoped. First, I was confused by the different decorator syntax I found in different examples, until I learned that decorators behave very differently when they take arguments. Then I tried decorating a method, and eventually learned that it wasn't working because I first have to turn my decorator into a descriptor by adding a __get__ method. During this whole process I've ended up confused more than a few times and still find that debugging this "decorated" code is more complicated than it normally is for Python. I'm now re-evaluating whether I really need decorators in my code, since my initial motivation was to save a bit of typing, not because there was anything that really required higher-order functions.

So my question is: should decorators be used liberally or sparingly? Is it ever more Pythonic to avoid using them?

A: 

If you have the same code at the beginning and end of many functions, I think that would justify the added complexity of using a decorator.

Rather like using a nice (but perhaps complex) template for a website with a lot of pages, it really saves time and adds clarity in the end.

gahooa
+10  A: 

Decorators are fine in their place and definitely not to be avoided -- when appropriate;-). I see your question as meaning essentially "OK so when are they appropriate"?

Adding some prefix and/or postfix code around some but not all methods of some classes is a good example. Were it all methods, a class decorator to wrap all methods would be better than repeating @thisonetoo endlessly;-). If it's once in a blue moon then it's not worth refactoring out to wrappers (decorators or otherwise). In the middle, there's a large ground where decorators are quite suitable indeed.

It boils down to one of the golden rules of programming -- DRY, for Don't Repeat Yourself. When you see your code becoming repetitious, you should refactor the repetition out -- and decorators are an excellent tool for that, although they're far from the only one (auxiliary methods and functions, custom metaclasses, generators and other iterators, context managers... many of the features we added to Python over the last few years can best be thought of as DRY-helpers, easier and smoother ways to factor out this or that frequent form of repetition!).

If there's no repetition, there's no real call for refactoring, hence (in particular) no real need for decorators -- in such cases, YAGNI (Y'Ain't Gonna Need It) can trump DRY;-).

Alex Martelli
Thanks Alex. I'll also have a look at those other features you mentioned (auxiliary methods, context managers, etc.).
RexE
@RexE, you're welcome! Unfortunately there isn't much literature on refactoring in Python -- Fowler's site, http://www.refactoring.com/ , is not about Python but is still quite instructive.
Alex Martelli
+3  A: 

Alex already answered your question pretty well, what I would add is decorators, make your code MUCH easier to understand. (Sometimes, even if you are doing it only once).

For example, initially, I write my Django views, without thinking about authorisation at all. And when, I am done writing them, I can see which need authorised users and just put a @login_required for them.

So anyone coming after me can at one glance see what views are auth protected.

And of course, they are much more DRY and putting this everywhere.

if not request.user.is_authenticated(): return HttpResponseREdiect(..)

uswaretech
But couldn't you just write something like 'login_required(request)' in the first line of the function?
RexE
+1  A: 

Decorators are a way to hoist a common Aspect out of your code.

Aspect-Oriented Programming proponents will tell you that there are so many common aspects that AOP is essential and central. Indeed, you can read a silly debate on this topic here:

http://stackoverflow.com/questions/232884/aspect-oriented-programming-vs-object-oriented-programming

There are a few common use cases for AOP. You can read a few here:

http://stackoverflow.com/questions/20663/do-you-use-aop-aspect-oriented-programming-in-production-software

There a few cross-cutting concerns for which decorators are helpful.

  • Access Controls ("security") Authentication, Authorization, Permissions, Ownership

  • Logging (including Debugging aids and Auditing)

  • Caching (often an implementation of Memoization)

  • Some error handling might be a common aspect and therefore suitable for decorator implementation.

There are very few other design patterns that are truly cross-cutting and deserve an AOP decorator.

S.Lott