Christopher Schmidt explains the traditional approach to wrapping functions and methods, one I use regularly; Python's built-in property function, as a decorator, produces read-only properties, but can provide read-write property access when used traditionally.
Are decorators merely cosmetic? I'm of the opinion that some syntaxes are better than others. You're likely to agree that:
>>> 1 + 2 3
is more concise, readable, and intuitive than
>>> int(1).__add__(2) 3
but may not agree that Python's decorators are a syntactic improvement. PEP 318 was hotly debated, but is final; decorators are in, and they'll be expanded in 3.0.
The motivation for decorators is compelling:
The current method of applying a transformation to a function or method places the actual transformation after the function body. For large functions this separates a key component of the function's behavior from the definition of the rest of the function's external interface. For example:
def foo(self): perform method operation foo = classmethod(foo)This becomes less readable with longer methods. It also seems less than pythonic to name the function three times for what is conceptually a single declaration. A solution to this problem is to move the transformation of the method closer to the method's own declaration. The intent of the new syntax is to replace:
def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)with an alternative that places the decoration in the function's declaration:
@classmethod @synchronized(lock) def foo(cls): pass
Even if calling code isn't exactly broken, wrapping a function more than likely changes the function's signature in some way; keeping all signature specification (such as it is in Python) at the head of a function is a good thing and requires some syntax like that of PEP 318. GIS programmers who've come to Python in the past several years via ArcGIS should get with @. If you can't or won't, that's fine too; there's another way, as Christopher shows.
On "prettier code": all else being equal, prettier code is more readable code. It's code that can teach, that can be more easily modified by others. In some ways, better code.
One downside of the decorator syntax: ability to test decorators in a doctest eludes me. The following:
def noisy(func): """ >>> @noisy >>> print foo() Blah, blah, blah 1 """ def wrapper(*args): print "Blah, blah, blah" return func(*args) return wrapper @noisy def foo(): return 1
fails:
Exception raised:
Traceback (most recent call last):
...
@noisy
^
SyntaxError: unexpected EOF while parsing
Could be ignorance on my part.
disciplina:~ crschmidt$ python foo.py -v
Trying:
@noisy
def foo():
return 1
Expecting nothing
ok
Trying:
foo()
Expecting:
Blah, blah, blah
1
ok
1 items had no tests:
__main__
1 items passed all tests:
2 tests in __main__.noisy
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
So, I guess I don't know what you're trying to do.>>> @noisy >>> def foo(): ...is invalid Python, which is obvious if you type it into a prompt:
>>> @noisy ...Thanks for the help, Christopher. And yes, best to define the foo mock within noisy's docstring test.
Comments are closed after 13 days.
Some rights reserved 2008 by Sean Gillies.
1Re: More decoration
Christopher Schmidt, 2009-01-02T19:12:03Z
def noisy(func): """ >>> @noisy ... def foo(): ... return 1 >>> foo() Blah, blah, blah 1 """ def wrapper(*args): print "Blah, blah, blah" return func(*args) return wrapper