views:

1286

answers:

3

I still haven't got my head around decorators in Python.

I've already started using a lot of closures to do things like customize functions and classes in my coding.

Eg.

class Node :
    def __init__(self,val,children) :
        self.val = val
        self.children = children

def makeRunner(f) :
    def run(node) :
        f(node)
        for x in node.children :
            run(x)
    return run

tree=Node(1,[Node(2,[]),Node(3,[Node(4,[]),Node(5,[])])])

def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)
printTree(tree)

As far as I can see, decorators are just a different syntax for doing something similar.

Instead of

def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)

I would write :

@makeRunner
def printTree(n) : print "%s," % n.val

Is this all there is to decorators? Or is there a fundamental difference that I've missed?

+11  A: 

No, you're spot on - the syntax is just syntactic sugar for foo = bar(baz) Here's a post on my blog that explains decorators in detail: http://fredrikholmstrom.com/python-decorators-explained/

thr
I know that decorators are initialized while the python interpreter is parsing the script. Is this the same with his hand made solution ? I am tempted to think that his way implies the custom function is created during execution so there is a slight difference. What do you think ?
e-satis
blog moved to:http://fredrikholmstrom.com/2008/09/python-decorators-explained/
Jaap
post moved again to: http://fredrikholmstrom.com/python-decorators-explained/ (sorry everyone ;()
thr
+5  A: 

Are your examples real code, or just examples?

If they're real code, I think you overuse decorators, probably because of your background (i.e. you are used to other programming languages)

Stage 1: avoiding decorators

def run(rootnode, func):
    def _run(node): # recursive internal function
        func(node)
        for x in node.children:
            _run(x) # recurse
    _run(rootnode) # initial run

This run method obsoletes makeRunner. Your example turns to:

def pp(n): print "%s," % n.val
run(tree, pp)

However, this ignores completely generators, so…

Stage 2: using generators

class Node :
    def __init__(self,val,children) :
        self.val = val
        self.children = children

    def __iter__(self): # recursive
        yield self
        for child in self.children:
            for item in child: # recurse
                yield item

def run(rootnode, func):
    for node in rootnode:
        func(node)

Your example remains

def pp(n): print "%s," % n.val
run(tree, pp)

Note that the special method __iter__ allows us to use the for node in rootnode: construct. If you don't like it, just rename the __iter__ method to e.g. walker, and change the run loop into: for node in rootnode.walker():
Obviously, the run function could be a method of class Node instead.

As you see, I suggest you use directly run(tree, func) instead of binding them to the name printTree, but you can use them in a decorator, or you can make use of the functools.partial function:

printTree= functools.partial(run, func=pp)

and from then on, you would just

printTree(tree)
ΤΖΩΤΖΙΟΥ
While using iterators is often nice, I find your rewrite much worse. It's _very non-obvious_ what "for item in child: # recurse" is doing, even with the comment. The original code using just functions is more obvious, and smaller.
James Antill
ΤΖΩΤΖΙΟΥ, it was a contrived example to illustrate my question; although based on similar things I've done in the past. Thanks for your alternatives. Interesting. I've certainly used the stage 1 form before, but don't think I've ever done tree-running with iter.
interstar
ACtually, looking at your last point, why would you prefer functools.partial to the decorator version? (ie. what are the pros and cons of each?)
interstar
@interstar: I fixed my last point wording; I was just offering an alternative to decoration.
ΤΖΩΤΖΙΟΥ
@James Antill: would another name for the iterator make it more obvious, as in `for item in child.walk_subtree()`? I suggested an iterator because it makes the rest of the code easier than passing function objects around, esp. if you want to keep context while walking the tree.
ΤΖΩΤΖΙΟΥ
+6  A: 

While it is true that syntactically, decorators are just "sugar", that is not the best way to think about them.

Decorators allow you to weave functionality into your existing code without actually modifying it. And they allow you to do it in a way that is declarative.

This allows you to use decorators to do aspect-oriented programming (AOP). So you want to use a decorator when you have a cross-cutting concern that you want to encapsulate in one place.

The quintessential example would probably be logging, where you want to log the entry or exit of a function, or both. Using a decorator is equivalent to applying advice (log this!) to a joinpoint (during method entry or exit).

Method decoration is a concept like OOP or list comprehensions. As you point out, it is not always appropriate, and can be overused. But in the right place, it can be useful for making code more modular and decoupled.

Dutch Masters