[Python-Dev] Re: Call for defense of @decorators

Andrew Durdin adurdin at gmail.com
Sun Aug 8 16:17:23 CEST 2004


Having just gone through the wiki page again, I think the last example
under A (quoted below) shows up very plainly the only really serious
problem with the before-def decorator syntax:

class TestDecorators(unittest.TestCase):

    ...

    def test_dotted(self):
        decorators = MiscDecorators()
        @decorators.author('Cleese')
        def foo(): return 42
        self.assertEqual(foo(), 42)
        self.assertEqual(foo.author, 'Cleese')

Suppose that you were not familiar enough with python to have heard of
decorators: looking at this code, would you have any idea that the @
statement (potentially) fundamentally affects the behaviour of the
foo() function? Is there anything except their proximity to suggest
that they are interdependent? The only implication that they *might*
be is the second assertEqual call, and that is by no means obvious.

On the other hand, the after-def (but before-colon) variants clearly
imply that the decorators are related to the function definition --
the reader might not understand their purpose, but they will not fail
to connect them with the function.

And here is a response to Guido's comments on the list-after-def
syntax in another thread that were then incorporated into the wiki
page.

Guido> - it hides crucial information (e.g. that it is a static method)
  after the signature, where it is easily missed

Not at all! The list of decorators would *be* a part of the signature.
The decorators will be no more easily missed than the name of the last
argument, which is arguably just as crucial.

Guido> - it's easy to miss the transition between a long argument list and a
  long decorator list

True. This is the main reason why having the list set off from the
argument list with a keyword or symbol would be preferred; but
suitable formatting would minimise the risk anyway -- see the example
further below.

Guido> - it's cumbersome to cut and paste a decorator list for reuse, because
  it starts and ends in the middle of a line

This is (IMHO) a very weak argument. A decorator list with one or two
decorators will probably be just as quick to type as to copy and paste
with the before-def syntax, when the time to scroll between lines is
taken into account (unless you're using a mouse, in which case
highlighting within a line is not slow at all). Moreover, in more
complicated decorator lists, the lists would likely be different
(reducing the advantage of copy+paste); or if not, if you regularly
use the same long decorator list, you're much better off defining your
own decorator which applies those from the list, and then using that
one repeatedly -- this will avoid errors and make maintenance easier.

Guido> Given that the whole point of adding decorator syntax is to move the
decorator from the end ("foo = staticmethod(foo)" after a 100-line
body) to the front, where it is more in-your-face, it should IMO be
moved all the way to the front.

I disagree. Having the decorator at the end, after the function body,
is obviously far from ideal, as it is extremely easy to miss; but the
decorators are not the most important part of the function definition.
The most important part (which should be most prominent, and thus
first) is the "def" keyword introducing the function definition, and
the function name. Whether the decorators are more or less important
than the arguments (and thus which should come first) is less certain.

The example of a long decorated function declaration that Guido gives was:

def longMethodNameForEffect(longArgumentOne=None,
                            longArgumentTwo=42) [
    staticmethod,
    funcattrs(grammar="'@' dotted_name [ '(' [arglist] ')' ]",
              status="experimental", author="BDFL")
    ]:

I don't think this is as clear as it could be (and an argument from
the "ugliness" of syntax should be using the clearest possible
formatting) -- breaking the line before the decorator list, and
indenting the decorator list one more level (to set it off from the
docstring/body) makes the whole much clearer, and prevents the
decorator list from being confused with the arguments:

def longMethodNameForEffect(longArgumentOne=None,
                            longArgumentTwo=42) \
        [staticmethod,
        funcattrs(grammar="'@' dotted_name [ '(' [arglist] ')' ]",
                  status="experimental", author="BDFL")]:

(Having the [ and ]: on their own lines at the same indentation level
may be clearer yet)

-- Andrew Durdin.


More information about the Python-Dev mailing list