[Python-Dev] Re: Decorators: vertical bar syntax (original) (raw)

Fernando Perez fperez528 at yahoo.com
Sun Aug 8 03:26:18 CEST 2004


Guido van Rossum wrote:

I'm including both of the following variants:

[Let's call this option #1]

|classmethod |accepts(int, int) |returns(float) def foo(arg1, arg2): ...

[And this option #2]

def foo(arg1, arg2): |classmethod |accepts(int, int) |returns(float) ...

In the discussion on decorators months ago, solutions involving special syntax inside the block were ruled out early on. Basically, you shouldn't have to peek inside the block to find out important external properties of the function. (Yes, I know that docstrings violate this principle. For the record, it would be better if they were prefix too; and a prefix decorator syntax will let us fix this in the future but introducing a "doc" decorator.)

Allow me to try and make a reasoned argument against your point, and in favor of #2 (as labeled above), even for docstrings. The reason why I find #1 more difficult to mentally parse, is that as you read the code, you need to accumulate information in your head which is not yet tied to anything in particular (the def line hasn't arrived yet).

In my brain, the process goes something like this (imagine this happening in code with multiple decorators, each with multiple lines of comments detailing its use):

  |classmethod

ok, a class method is coming, but I don't know yet what it's called. Put this on the 'mental stack', let's see where it goes...

  |accepts(int, int)

and it will take integer arguments; more for the stack...

  |returns(float)

and return a float; add to the stack...

  def foo(arg1, arg2):

Ah! It's function foo, with 2 args named arg1, arg2. Now I go back (mentally) and wrap together my knowledge about it, unwinding my stack, producing foo, a classmethod with arg1,arg2 as integers, and which returns a float.

I find this unpleasant and inefficient from a cognitive viewpoint, because our brain is best at processing information when it has 'buckets' where to put it: if you read the def line first, the function name becomes the 'bucket' where all further information goes. That's why it's always harder to remember random bits of information than things which connect to other things you already know.

For these reasons, I feel that option 2 is much more efficient. A similar outline of my mental process with option 2:

def foo(arg1, arg2):

Ok, a new function foo() goes in, it takes 2 args, arg1 and arg2. Make mental bucket labeled 'foo'. Anything that comes now, until we de-indent, goes into box 'foo'. No need to keep random bits of information up in the air until I know where they're supposed to go.

|classmethod

Store 'classmethod' in bucket 'foo'.

|accepts(int, int)

Store 'accepts(int, int)' in bucket 'foo'.

|returns(float)

Store 'returns(float)' in bucket 'foo'.

Done. All information is stored in mental bucket 'foo', where it belongs.

Bucket 'foo' was created when I saw the def line, nicely separated by indentation from all else, which is what my python mental processor is already well trained to do. Then, all the information that came afterwards (including possibly the docstring) fell nicely into that mental bucket.

Option 1, which you seem to favor, reminds me of a problem I felt when studying German for the first time (Spanish is my native language). German has these funny 'split-verbs', like anrufen, which are used in a sentence split in two, and with the initial part of the verb only appearing at the very end of the sentence. A simple example (I don't recall the really painful ones anymore) is "Ich rufe dich an". You don't know that the verb is anrufen, until you see the 'an' at the end of the sentence. With long sentences, I remember feeling very frustrated, because I esentially had to buffer the whole thing in my head with very little structure, waiting for the last bit to come in. Then I needed to quickly reconstruct the meaning, now that I knew which verb was being used, by mentally reparsing the whole thing. It's been many years, but I distinctly remember this as being a significant hurdle in spoken conversation in German.

I honestly feel adding this kind of process to python would be a significant reduction to the fluidity with which the language 'parses mentally'.

And I like the fact that this syntax:

def foo(...): @dec1 @dec2 "docstring"

# code begins...

sort of declares a 'header zone' for the function, which is visually well defined by indentation and string highlighting, but allows the 'def' part to be very easy to spot visually because it cuts the indentation cleanly. It feels like a quite natural convention for me to think that until I see the closing of the docstring, I'm in the 'header' or 'interface' zone of the function.

I hope I've at least presented a clear argument in defense of this choice. Whether you agree or not, is up to you. I deliberatly didn't mention the @ vs | issue, since for this discussion it's completely irrelevant.

Best,

f



More information about the Python-Dev mailing list