views:

173

answers:

2

Editing because the initial code was confusing.

I would assume these two things to be same,

#I would use either of these
#Option 1
def bar(*args):
    pass
foo = deco(bar)

#Option2
@deco
def foo(*args):
    pass

However if the decorators deco has side effects, this is not guaranteed. In partcicular, this was my ecpectation form a decorator(no side effect), and I came across one with side effct and was bitten by it,

#Option1
def bar(*args):
    pass
foo = register.filter(bar)

#Option 2
@register.filter
def foo(val, arg):
    pass

So is my expectation wrong, or is django being inconsistent with the best practices?

+2  A: 

Actually, these both are exactly the same:

def foo(*args):
    pass
foo = deco(foo)

@deco
def foo(*args):
    pass

If you want to decorate bar and call it foo, foo = deco(bar) is the right way. It says: "decorate this previously defined thing called bar and call it foo". The point of the decorator syntax is to state the wrapping function before the definition, not to rename it.

Unless you need to use bar later, there is no reason to call the undecorated function with a different name. By doing this you lose precisely the ability to use the decorator syntax sugar.

deco doesn't need to be a function. It can be an object with a __call__ method, which is useful precisely to encapsulate side effects.

Roberto Bonvallet
@roberto: exactly. My quwstion is, should it also be exactly the same as `foo = deco(bar)`
uswaretech
What the heck is `bar`? Decorator `deco` wraps method `foo`, and is equivalent exactly to `foo = deco(foo)`. Why do you keep asking about `foo = deco(bar)`? Perhaps this misconception of yours is the fundamental issue.
Paul McGuire
bar is another callable. I want to bind the retirn value of deco(bar) to foo.
uswaretech
I edited my answer to cover the `foo = deco(bar)` case.
Roberto Bonvallet
Edited again after question edition.
Roberto Bonvallet
A: 

Your examples do not express the same things in every case! Why do you insist on using bar?

Take your first example:

#Option 1
def bar(*args):
    pass
foo = deco(bar)

#Option2
@deco
def foo(*args):
    pass

Option 1 does (literally)

foo = deco(bar)

but Option 2 is the equivalent of

foo = deco(foo)

Can't you see the difference there?

So, in short, yes: your assumption and your expectations are wrong.

If you need the undecorated version of your function, as well as the decorated one, just save it beforehand:

def foo(*args):
    pass
bar = foo
foo = deco(foo)
hop
> Why do you insist on using bar? Because I need access to both decorated and undecorated function.My assumptions are (obviously) wrong. But why is my expectation that it be side-effect free wrong?
uswaretech
why would you restrict yourself with such an arbitrary rule?
hop
does Library().register() even do something to func, except registering it? foo should be the same before and after...
hop
look at template/defaultfilters.py: you don't even have to use the return value of register.filter()!
hop