Decorator

L

Lad

I use Python 2.3.
I have heard about decorators in Python 2.4.
What is the decorator useful for?
Thanks for reply
L.
 
S

Sybren Stuvel

Lad enlightened us with:
I use Python 2.3.
I have heard about decorators in Python 2.4.
What is the decorator useful for?

A whole lot of stuff. I've used them for:
- Logging all calls to a function, including its arguments.
- Ensuring there is a database connection before the function is
called.
- Casting the arguments to certain types before passing them to
the function.

And there is much more possible...

Sybren
 
B

bruno at modulix

Lad said:
I use Python 2.3.
I have heard about decorators in Python 2.4.

What Python 2.4 adds is only syntactic sugar for decorators. You can do
the same - somewhat more explicitely - in 2.3.
What is the decorator useful for?

FWIW, I'm not sure the name 'decorator' is such a great idea. A
decorator (read 'function decorator') is mainly a callable that takes a
callable as param and returns another callable, usually wrapping the
passed callable and adding some responsabilities (tracing, logging,
pre-post condition checks, etc). Two well-known examples are classmethod
and staticmethod.

The whole things looks like this:

def deco(func):
print "decorating %s" % func.__name__
def _wrapper(*args, **kw):
print "%s called " % func.__name__
res = func(*args, **kw)
print "%s returned %s" % (func.__name__, str(res))
return _wrapper

# python < 2.4
def somefunc():
print "in somefunc"
return 42

somefunc = deco(somefunc)

The syntactic sugar added in 2.4 allows you to avoid the explicit call
to deco():

# python >= 2.4
@deco
def someotherfunc():
return "the parrot is dead"


As you see, apart from the syntactic sugar, there's nothing new here.
It's just plain old higher order function, well known in any functional
language.

HTH
 
M

Martin Blume

What Python 2.4 adds is only syntactic sugar for decorators.
You can do the same - somewhat more explicitely - in 2.3.



The whole things looks like this:

def deco(func):
print "decorating %s" % func.__name__
def _wrapper(*args, **kw):
print "%s called " % func.__name__
res = func(*args, **kw)
print "%s returned %s" % (func.__name__, str(res))
return res
^^^^^^^^^^
Shouldn't here be a return res, so that wrapper
behaves like the original function?
return _wrapper

# python < 2.4
def somefunc():
print "in somefunc"
return 42

somefunc = deco(somefunc)

Thanks for the explanation.


Another question: Isn't decorating / wrapping usually
done at runtime, so that the @deco notation is pretty
useless (because you'd have to change the original
code)?
What do I miss here?

Martin
 
S

Sybren Stuvel

Martin Blume enlightened us with:
Another question: Isn't decorating / wrapping usually done at
runtime, so that the @deco notation is pretty useless (because you'd
have to change the original code)?

Please explain why that would make the @deco notation pretty useless.

Sybren
 
B

bruno at modulix

Martin said:
(snip)

return res
^^^^^^^^^^
Shouldn't here be a return res, so that wrapper
behaves like the original function?

oops, my bad :(
And yes, of course.
Thanks for the explanation.


Another question: Isn't decorating / wrapping usually
done at runtime,

It is. I mean, using decorator syntax, this is done during import, which
happens at runtime.
so that the @deco notation is pretty
useless (because you'd have to change the original
code)?

I don't understand your question.
What do I miss here?

Ok, I get it (well, I think...).

The use case for @decorator is for wrapping functions or method *in the
module/class itself*. It's not for module client code (but this of
course doesn't prevent client code to dynamically add other wrappers...)

One of the primary use case makes this pretty clear IHMO : classmethod
and staticmethod :

# python < 2.4:
class Cleese(object):
def doSillyWalk(cls):
pass
doSillyWalk = classmethod(doSillyWalk)

# python >= 2.4:
class Cleese(object):
@classmethod
def doSillyWalk(cls):
pass

Another example : CherryPy uses decorators to mark methods which are
'published' (ie: are action controllers responding to a given URL)


HTH
 
M

Martin Blume

Martin Blume enlightened us with:
Don't know if I enlightened anybody ... :)
Please explain why that would make the @deco
notation pretty useless.
Well, if you're changing the original module, you
might as well insert the needed functionality in
the original function, no?
Or rename the original function, write a function
having this original name and calling from it the
original functionality?

Isn't the point of a decorator to change the
behavior externally, at runtime, possibly changing
it in different ways at different places at different
times?

So why this @deco notation? Can you apply it externally?
Meaning to
import module
first, then
@deco(module.func)
somewhere later?


Martin
 
M

Martin Blume

[snip]

The use case for @decorator is for wrapping functions
or method *in the module/class itself*.
That was the question. What's the use of doing it
like that in the module *itself* (I mean, you change
directly the original function)?
It's not for module client code (but this of
course doesn't prevent client code to dynamically
add other wrappers...)
How do the clients it? The "oldfashioned"
deco(doSillyWalk)
way?

Martin
 
B

bruno at modulix

Martin said:
Don't know if I enlightened anybody ... :)

Not sure...

But let's hope someone else having doubts about @decorator will find
this thread, so we won't have to point him/her to the documentation.
Well, if you're changing the original module,

Who's talking about "changing the original module" ?
you
might as well insert the needed functionality in
the original function, no?

higher order functions allow to keep orthogonal responsabilities
separated.

(snip)
Isn't the point of a decorator to change the
behavior externally, at runtime, possibly changing
it in different ways at different places at different
times?

You're confusing the python specific @decorator syntax with the OO
design pattern by the same name. This syntax is purely syntactic sugar
for a specific use case of higher order functions.
So why this @deco notation?

To improve readability.

@decorator
def my_one_hundred_locs_func():
...

is much more readable than:
def my_one_hundred_locs_func():
...
# 100 LOCS later
my_one_hundred_locs_func = decorator(my_one_hundred_locs_func)
Can you apply it externally?

No. It doesn't make sens to replace:
mymodule.func = decorator(mymodule.myfunc)
with
@decorator
mymodule.func

Note that all this should be clear for anyone having read the doc...

HTH
 
M

Martin Blume

"bruno at modulix" schrieb
Who's talking about "changing the original module" ?
Well, you have to apply @deco in the module where
func_to_decorated is placed.
You're confusing the python specific @decorator
syntax with the OO design pattern by the same name.
This syntax is purely syntactic sugar
for a specific use case of higher order functions.
Yes, that explains my confusion.
To improve readability.

@decorator
def my_one_hundred_locs_func():
...

is much more readable than:
def my_one_hundred_locs_func():
...
# 100 LOCS later
my_one_hundred_locs_func = decorator (my_one_hundred_locs_func)
That makes sense.
Note that all this should be clear for anyone having
read the doc...
<blush>
Errm, yes, you're so right.

Thanks for reading the documentation to me
and clearing this up :)

Martin
 
B

bruno at modulix

Martin said:
[snip]

The use case for @decorator is for wrapping functions
or method *in the module/class itself*.

That was the question. What's the use of doing it
like that in the module *itself*
Readability.

Since the decoration (in the module) is somehow part of the function
definition, it's more obvious to have it expressed with a modifier-like
syntax at the top of the def statement than expressed as a function call
and rebinding after the end of def block. When reading the code, with
the @decorator syntax, the use of the decorator is pretty evident.

Once again, this is nothing more than syntactic sugar - but syntactic
sugar counts. FWIW, function decorators seems to be *much* more used
since the introduction of the @decorator syntax, when you could do the
same thing since the introduction of nested scopes and closures in
Python (dont remember the version, but this is not really new).
(I mean, you change
directly the original function)?

If you mean that the code added by the decorator could be injected
directly in the function, that's not always true (ie: classmethod and
staticmethod for example), and it just plain sucks anyway - you don't
write a decorator for a single function, you write it to separate
orthogonal concerns, like tracing, handling auth, partial application of
function, etc...
How do the clients it?

Like it did since there are nested scopes and closures in Python. Higher
order functions are not really something new, you know - Lisp had them
way back in 1958 !-)
The "oldfashioned"
deco(doSillyWalk)
way?

from monty.python import Cleese
Cleese.doSillyWalk = deco(Cleese.doSillyWalk)

HTH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,297
Messages
2,571,529
Members
48,241
Latest member
PorterShor

Latest Threads

Top