More decoration
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:
is more concise, readable, and intuitive than
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.
Comments
Re: More decoration
Author: Christopher Schmidt
Re: More decoration
Author: Sean
Moving foo inside the docstring doesn't help. It was being found by doctest before.Re: More decoration
Author: Christopher Schmidt
I guess I don't know what you're trying to do. I'm not trying to test 'foo', I'm trying to test 'noisy'. So I define a 'foo' that uses 'noisy', and I test that 'foo' does what I want. (In this case, foo does nothing except return '1'; this would still be the case even if the real 'foo'. So, I guess I don't know what you're trying to do.Re: More decoration
Author: Sean
Ah, I finally see what's up. My original docstring text is invalid Python, which is obvious if you type it into a prompt: Thanks for the help, Christopher. And yes, best to define the foo mock within noisy's docstring test.