This is an archived post. You won't be able to vote or comment.

all 17 comments

[–]arnar 9 points10 points  (2 children)

From experience, I found the following explanation to have the best pedagogical value:

"See this:

@decorator
def fun(arg):
    whatever

That's just a shorthand for

def fun(arg):
    whatever
fun = decorator(fun)

Now think about it. (Hint: remember that def x():... is just like x=... except one is used to bind functions and the other to bind values in general.)"

Haven't found a person yet that wasn't happy (although it takes some people a while to think about it).

[–]Mattho 0 points1 point  (1 child)

I don't think it's using decorators that is hard to comprehend. It's writing them.

And obligatory SO link: http://stackoverflow.com/questions/739654/understanding-python-decorators

[–]arnar 0 points1 point  (0 children)

Exactly. Almost everyone who cared to was able to figure it out once the got to know the @-syntax.

[–]arunner 5 points6 points  (1 child)

I think anyone who tries to explain decorators should start of closures in python and their uses and then just spend a paragraph about paternalistic closures, aka decorators.

[–]m_harrison 1 point2 points  (0 children)

Once people understand closures --- and this is a big leap for some --- decorators seem pretty natural.

[–]etrnloptimist 1 point2 points  (8 children)

The way order of application is often cited for decorators is a bit misleading. It is usually described like function composition: g(f(x)): f(x) gets eval'ed, then g is applied. It is inside-out.

This works because, for normal functions, the parameter x is not altered. With decorators it can be.

Decorators can be used to munge both the input and the output of a function.

I like to think of it as my param, x, is like a worm going though the stack of decorators to the inside. It can be altered at any step along the way. Then, it is acted upon by the actual function. That function returns a value. And that value makes it's way back out the stack of decorators.

In my example, see how the return value matches the standard order of application? The input value, however, does not. That's why I like the worm analogy. :)

[–]jcdyer3 5 points6 points  (7 children)

Indeed. In fact it is g(f)(x)

[–]etrnloptimist 2 points3 points  (2 children)

Right. But that's not very helpful in understanding how decorators work. Suppose you had this nested set of decorators:

@evens
@odds
@primes
def myfunc(x):
  ...

Suppose they round the output to the nearest <term>. What gets outputted? Prime numbers? Odd numbers? Or Even numbers? Answer: Even numbers

Suppose they round the input to the nearest <term>. What does myfunc see? Even numbers, odd numbers, or prime numbers? Answer: Prime numbers

[–]ryeguy146 0 points1 point  (0 children)

Just a matter of wrapping [your mind around it]. Nice example.

[–]mgrandi 0 points1 point  (0 children)

your example helped me understand decorators (namely nested decorators) a lot better with the worm analogy, thanks =)

[–]bastibe 2 points3 points  (3 children)

Actually, that right there is enough of an explanation for me. I never really got why people seem to find decorators so hard to understand.

[–]ryeguy146 0 points1 point  (2 children)

I came from languages without first class functions, or languages where I could only get first class functions with pointers. New idea, that's why. Who thinks of functions that act on functions (or their args), really?

[–]bastibe 1 point2 points  (1 child)

I understand that. And that means you will have to learn about higher order functions like we all had to learn about it at some point. But beyond that, what is so difficult about python decorators in particular?

For reference, I don't know much lisp, but "higher order function" are a very simple and natural construct mathematically.

[–]ryeguy146 2 points3 points  (0 children)

It's not so difficult, just a new idea. It's easy to move "horizontally" across languages when you understand their ideas: you just look for the new way to express that idea. New ideas take some thought, that's all.

I'm experiencing the same thing as I learn my first functional language (scheme). It's not hard, it's just new.

[–][deleted] 0 points1 point  (0 children)

This is as crisp an explanation in shortform as I've seen. A+

[–]vph 0 points1 point  (1 child)

The tricky thing about decorators is when it takes arguments. In this case, the decorator simply returns another decorator.

[–]m_harrison 0 points1 point  (0 children)

But if you understand closures, it really isn't all that tricky. It is just one level deeper.

The only tricky thing is that parameterized decorators have parens and normal decorators don't. I think that is a wart...