@decorators

D

daishi

For what it's worth:

As far as I know, the proposed @decorator syntax will be the first
time that two logical lines of python with the same indentation will
not be independent of one another. Previously, when looking at:

some_python(code)
and_some_more = stuff

there was no need to look at the the first line in order to know what
the second line meant/did (and vice versa). It would seem that the
cases when there are effects across multiple logical lines is captured
in compound statements:

"""
Compound statements contain (groups of) other statements; they affect
or control the execution of those other statements in some way.
""" -Python Reference Manual

My understanding of decorators is that they fit this definition.
One thing that I am not entirely clear about is whether decorators
are intended to be a compound statement themselves, or whether they
are meant to simply be extensions of current compound statements
such as function definition (and class definitions?)

In either case, however, it seems that the following should apply:

"""
Each clause header begins with a uniquely identifying keyword and ends
with a colon. A suite ... can be one or more indented statements on
subsequent lines.
""" -Python Reference Manual

In the first case where decorators form their own compound statement,
this would seem to imply the basic layout of:

@decorator:
def foo():
pass

(Whether that uniquely identifying keyword for decorators happens to
be spelled '@' is not important to me.)

In the second case where decorators are simply extensions of current
compound statements, the current wording of the Python Reference
Manual would seem to imply that for function definition the clause
header must begin with 'def'. I.e., the decorator should not come
before the def, and certainly not on a separate line. Beyond this,
however, for me it is not particularly important whether the
decorator comes before/after the function name/argument list, and
how it is delimited.

I guess the basic point that I am trying to make is that what I find
important is consistency with the basic visual layout of code promised
(IMHO) by python. I believe that this promise is violated by the
currently proposed decorator scheme.

d

PS: Although I believe the current view of the implementors is to view
decorators as an extension to function definition, I believe that the
separate compound statement view is fairly rich. As others have noted,
it can make identical decorations simpler:

@static,synchronized:
def foo():
pass
def bar():
pass
 
D

David Fraser

daishi said:
For what it's worth:

As far as I know, the proposed @decorator syntax will be the first
time that two logical lines of python with the same indentation will
not be independent of one another. Previously, when looking at:

some_python(code)
and_some_more = stuff

there was no need to look at the the first line in order to know what
the second line meant/did (and vice versa). It would seem that the
cases when there are effects across multiple logical lines is captured
in compound statements:

"""
Compound statements contain (groups of) other statements; they affect
or control the execution of those other statements in some way.
""" -Python Reference Manual

My understanding of decorators is that they fit this definition.
One thing that I am not entirely clear about is whether decorators
are intended to be a compound statement themselves, or whether they
are meant to simply be extensions of current compound statements
such as function definition (and class definitions?)

In either case, however, it seems that the following should apply:

"""
Each clause header begins with a uniquely identifying keyword and ends
with a colon. A suite ... can be one or more indented statements on
subsequent lines.
""" -Python Reference Manual

In the first case where decorators form their own compound statement,
this would seem to imply the basic layout of:

@decorator:
def foo():
pass

(Whether that uniquely identifying keyword for decorators happens to
be spelled '@' is not important to me.)

In the second case where decorators are simply extensions of current
compound statements, the current wording of the Python Reference
Manual would seem to imply that for function definition the clause
header must begin with 'def'. I.e., the decorator should not come
before the def, and certainly not on a separate line. Beyond this,
however, for me it is not particularly important whether the
decorator comes before/after the function name/argument list, and
how it is delimited.

I guess the basic point that I am trying to make is that what I find
important is consistency with the basic visual layout of code promised
(IMHO) by python. I believe that this promise is violated by the
currently proposed decorator scheme.

d

PS: Although I believe the current view of the implementors is to view
decorators as an extension to function definition, I believe that the
separate compound statement view is fairly rich. As others have noted,
it can make identical decorations simpler:

@static,synchronized:
def foo():
pass
def bar():
pass
I like this idea. What about using the list syntax instead of the @ syntax:

[decorator]:
def foo():
pass

[static, synchronized]:
def foo():
pass
def bar():
pass

[static,
synchronized,
types(int, int),
returns(None)
]:
def foo():
pass

This would then take away the argument against a valid list before a def
being valid python but not previously having any effect...

Or for good measure you could add a keyword:

declare [static, synchronized, types(int, int), returns(None)]:
def foo():
pass
def bar():
pass

Now that looks pretty neat

David
 
J

John Roth

daishi said:
For what it's worth:

As far as I know, the proposed @decorator syntax will be the first
time that two logical lines of python with the same indentation will
not be independent of one another.

Examples:

try - except - finally
if - elif - else
while - else

John Roth
 
T

Tom B.

daishi said:
For what it's worth:

As far as I know, the proposed @decorator syntax will be the first
time that two logical lines of python with the same indentation will
not be independent of one another. Previously, when looking at:

I think the @decorator syntax was invented by Microsoft to sabotage Python,

Tom
 
D

David Fraser

John said:
Examples:

try - except - finally
if - elif - else
while - else

I think he mean two consecutive lines of python code with the same
indentation.

David
 
N

Neil Hodgson

daishi:
As far as I know, the proposed @decorator syntax will be the first
time that two logical lines of python with the same indentation will
not be independent of one another. Previously, when looking at:

some_python(code)
and_some_more = stuff

there was no need to look at the the first line in order to know what
the second line meant/did (and vice versa).

Much of the time, consecutive lines with the same indentation are highly
dependent with earlier lines modifying the behaviour of successor lines:

global x
x = 9

Neil
 
T

Tor Iver Wilhelmsen

Neil Hodgson said:
global x
x = 9

I think what he meant was that the decorators in effect make for
"hidden continuation", e.g.

@foo
def fooDecorated():
pass

is like

@foo def fooDecorated():
pass:

when other line continuations are explicit.
 
T

Tom B.

Paul McGuire said:
Heeey, let's keep it civil!!!
Your right, I apologize, @decorator syntax will affect me with much less
adversity than Microsoft.

Tom
 
M

Mark Bottjer

Tor said:
I think what he meant was that the decorators in effect make for
"hidden continuation", e.g.

@foo
def fooDecorated():
pass

is like

@foo def fooDecorated():
pass:

when other line continuations are explicit.

Which is *exactly* why I dislike this syntax so much. I don't mind the
"@", though it does look a might out-of-place. What I dislike is the
placement: It relies on setting a "magic state" in the interpreter to
affect the very next thing at the same indentation level. I can't think
of any other place in Python where this sort of thing is done.
Everything else either introduces a new block, or requires bracketing.

Everyone shows the examples like above, but what about the following?

class C:
# This is a really long comment that has little to do
# with the following function; it is here only so that
# the comment wraps. This is a really long comment that
# has little to do with the following function; it is
# here only so that the comment wraps over several lines.
@ staticmethod

# MAB 20040806113000: Added argument baz.
# MAB 20040806112957: Added argument bar.
# MAB 20040806112950: Initial version.
def static_method_foo( bar, baz):
pass

Did you catch it? This method *is* decorated, though it is hard to see
it unless you are really looking for it. Syntax coloring will help, of
course, but not much, and we shouldn't rely on that anyway.

My concern is that the decorator can be separated from the decorated by
an arbitrarily large amount of space. Even ONE blank line is enough for
me to mentally dissociate the decorator from the decorated. Comments
make it worse. Now, would a sane programmer do this? One would hope not,
but there is nothing to prevent it--and I've found that the definition
of "sane" varies widely between programmers.

What I think might work better is to treat these decorators the same way
we already treat docstrings.

def decorated( a, b, c):
@classmethod
@signature( None, a=int, b=float, c=dict)
@transactional( database)
"""\
decorated( a, b, c): A thrice-decorated function.

The silly function does nothing except get "decorated." classmethod
determines the form of the first argument (class, object, or
other). signature changes the call behavior, asserting the types
passed to and returned from the function. Finally, transactional
causes boilerplate code to be executed before and after the function
body, ensuring that either all changes to database are applied, or
none are. Seeing as this function does nothing, this is trivial :).
"""

pass

Personally, I kind of like the "@" syntax in this form. It still sticks
out (and IMHO, it *should* stick out, because it is not a normal
statement), but it also is easier to ignore if you aren't looking for it
because it is explicitly scoped at the function level.

We already expect metadata--namely, the docstring--to be at the top of
the function. This simply extends that idea to include other forms of
metafunctionality. The compiler already looks for metadata at the start
of functions; the special syntax helps the compiler determine which
statements need to be handled immediately, and where the function
implementation starts.

-- Mark
 
P

Peter Hansen

Mark Bottjer wrote:

Mark, would your example still be acceptable to you if someone wrote
it like this?:
def sillyFunction( a, b, c):
"""\
The silly function does nothing except get "decorated." classmethod
determines the form of the first argument (class, object, or
other). signature changes the call behavior, asserting the types
passed to and returned from the function. Finally, transactional
causes boilerplate code to be executed before and after the function
body, ensuring that either all changes to database are applied, or
none are. Seeing as this function does nothing, this is trivial :).
"""
@classmethod
> # PLH 20040806112957: added new decorator here
# PLH 20040806130023: also changed last value
@signature( None, a=int, b=float, c=dict)
@transactional( database)
pass

While I find merit in your idea, it's not entirely fair to discard
the @ syntax because it *can* be written the way you showed, but then
not to perform the same analysis on your own idea.

To be entirely fair, I'm not sure that *any* of the proposed syntaxes
cannot be written in such a way that they get buggered up by intervening
whitespace and comments... nor should this be a sole reason for
discarding any one.

-Peter
 
M

Mark Bottjer

David said:
I like this idea. What about using the list syntax instead of the @
syntax:

[static, synchronized, types(int, int), returns(None) ]:
> def foo():
pass

<rant level="hysterical">
No, no, NO! Lists are value expressions: not statements, not
declarators, and most certainly not suites!!!
</rant>

Sorry, just had to get that out of my system... :)

Seriously, though, one problem I see is that this can get obfuscative
really fast as the number or complexity of decorators grows:

[static,
synchronized,
types(int, int),
returns(None),
yet,
more,
decorators,
go,
here,
making(this),
quite,
the( (pain, to, read, past))
]:
def foo():
pass

By the time I've read all of that, I've likely forgotten what it was I
was looking for in the first place. Of course, the @ syntax as it stands
has the same problem, but at least it doesn't introduce spurious
indentation in the process.
This would then take away the argument against a valid list before a
def being valid python but not previously having any effect...

Right. At the expense of a grammar that is nonsensical when viewed in
the context of how those symbols work elsewhere in the language. Here we
have a list (okay), containing a bunch of function names (okay), which
get called via side-effect on something not explicitly indicated
(er...), and starts a block of code (say what???).

The (){}[] symbols all already *mean* something in python (often more
than one thing, actually). The @ doesn't mean anything yet, so it is a
perfect candidate for a completely new concept such as this. I just
think that they put it in the wrong spot.
declare [static, synchronized, types(int, int), returns(None)]:
> def foo():
> pass
> def bar():
> pass

People keep suggesting this one, and I just don't see it. Yes, it's
shorter (marginally, sometimes). But is it really clearer? I don't think
so. Now, instead of just having to look at the preceding few lines to
figure out what decorators are being used, you have to look up an
arbitrary number of lines. Not fun when one's functions start getting
real-world sized.

I understand the desire to reduce the redundancy, but even this small
example shows a fundamental problem with this approach. In all but the
most simple cases, most functions will end up with *different* sets of
decorators. Case in point: both foo and bar above take (int, int) and
return None. Such parity rarely survives in the wild. This leaves us
with even more of a mess:

declare [static, synchronized]:
declare [returns(None)]:
declare [types(int)]:
def foo(a):
pass
declare [types(int, int)]:
def bar(a, b):
pass
declare [returns(int)]:
def baz():
pass

I *shudder* to think of trying to sort this out when these functions
contain real code.

(FYI: None of this is directed at you, personally. Your post just
happened to be the first post I found suggesting this kind of syntax.)

-- Mark
 
M

Mark Bottjer

David said:
I think he mean two consecutive lines of python code with the same
indentation.

That's how I took it. From another angle, each of the clauses in
try/except/finally et al start a suite (i.e., end in ':'); @dec does
not. Except for the funny symbol, it looks just like any other
statement--but it sure doesn't act like one.

-- Mark
 
T

Terry Reedy

David Fraser said:
John Roth wrote:

I think he mean two consecutive lines of python code with the same
indentation.

And the same examples apply:

if like(atdeco): print 'hooray'
else: print 'boo'

Terry J. Reedy
 
M

Mark Bottjer

Peter said:
Mark Bottjer wrote:

Mark, would your example still be acceptable to you if someone wrote
it like this?:



# PLH 20040806112957: added new decorator here

FWIW, I considered adding this case, but thought the post was long
enough already. And I knew someone would bring me to task on it in short
order anyway. :)

Of course it is still hard to read, but I personally find it easier than
the prefix syntax for one main reason: the decorators are confined
inside the function they modify. If the language were to state (for
example) that all decorators are to appear between the def line and the
optional docstring, than it becomes easy (easier, anyway) to parse, and
for the programmer to navigate: find function def; start looking for
meta stuff; stop looking at the first non-meta statement. No look-ahead
is required, and no buffering for a declaration not yet started.
While I find merit in your idea, it's not entirely fair to discard
the @ syntax because it *can* be written the way you showed, but then
not to perform the same analysis on your own idea.

I wasn't attempting to discard it. I was attempting to show a border
case I had not seen discussed anywhere (as others have done with the def
foo (ARGS) [DECS]: variants). To that end, I tried to show just how bad
it could be. To make sure it had been considered was the sole intent.
To be entirely fair, I'm not sure that *any* of the proposed syntaxes
cannot be written in such a way that they get buggered up by intervening
whitespace and comments... nor should this be a sole reason for
discarding any one.

Agreed. I *personally* find it easier to understand code with the
decorators infixed than with them prefixed. To me, knowing that
everything I want to know about a function is somewhere *in* that
function, as opposed to somewhere *near* it, is quite a powerful aide to
understanding. YMMV.

-- Mark
 
D

daishi

Terry Reedy said:
And the same examples apply:

if like(atdeco): print 'hooray'
else: print 'boo'

Terry J. Reedy

I apologize if I've phrased things poorly/imprecisely,
but in large part your example exemplifies the point that
I was trying make in my previous message: decorators seem
like they belong in or are themselves compound statements.
The current proposed formatting seems to me like:

else print 'boo'
if like(atdeco): print 'hooray'
 
D

Dominic

I do not know much about decorators yet,
but how about a decorator module for
default stuff e.g. synchronized etc.?
Does this make sense?

Ciao,
Dominic
 
H

Hallvard B Furuseth

Neil said:
daishi:


Much of the time, consecutive lines with the same indentation are highly
dependent with earlier lines modifying the behaviour of successor lines:

global x
x = 9

Poor example. They are visually tied together in that they both mention
the variable x. '@decorator's do not mention the function they affect.
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top