Proposed improved decorator syntax

B

barnesc

I'm revising my vote.

Thanks for the responses.

I vote against:

class foo:
@synchronized
@types("o,i,i")
@author('Chris King')
def introduceNewFeature(self, someArgument, anotherArgument):
pass # whatever

Pros:
- Allows arbitrary functions to be applied as decorators.

Cons:
- Places decorator metadata before the function (in contrast with docstrings).
- Hides function name.
- @ symbols are ugly.
- @ symbols are a particular hack for creating function metadata.
- More such hacks may be needed in the future.
- @ can be used to store metadata, but in a convoluted way:
@name('Value') works, if function 'name' is defined (imports must be used
for large projects); however the metadata name actually stored may be
different than 'name' (ie no symmetry, no ease of use).
- @ will be used for metadata, if no other solution is provided.

Recognize that we're *really* trying to create function metadata. A more
flexible solution is to make a metadata dictionary be part of function and
class definitions. Then 'decorator' simply becomes one name among many
different metadata names.

I vote for:

---------------------------------------------------------------------
Proposed Syntax
---------------------------------------------------------------------

class foo:
def introduceNewFeature(self, someArgument, anotherArgument):
.decorate = [synchronized]
.accepts = (int, int)
.author = 'Chris King'
pass # whatever

Use introduceNewFeature.__meta__ to access the metadata dict.
The .accepts meta-datum calls isinstance() for each argument.
Use 'object' for arguments that can be any type.

Pros:
- Readable.
- No ugly @ symbols.
- Metadata is placed after the function name.
- .decorate is semantically superior to @.
- Similar to existing Python syntax (equals sign and dots).
- Allows metadata to be created and read (symmetrically, easily).
- Allows arbitrary functions to be applied as decorators.
- Code is ordered correctly (function declaration before docstring and
metadata).

Cons:
- Name conflicts between builtin metadata names (eg 'decorate') and
user metadata names (eg 'decorate').

Solutions to Name Conflicts:
- Name conflicts are a small problem if the set of Python special
metadata names is kept small and well documented (eg '.decorate'
and '.accepts' can be documented as 'metadata keywords', to be
avoided).
- The paranoid can prefix metadata names with underscores.
- Alternatively, .__decorate__ and .__accepts__ can be used.
- Alternatively, {'decorate': [synchronized], 'accepts': (int, int)}
can be used as the first line of metadata, which is interpreted
as a 'Python special metadata dict'.

---------------------------------------------------------------------
Conclusion
---------------------------------------------------------------------

Either attack the full problem of metadata, or don't.

@ is a hack. @ looks ugly. @ is a particular solution for the general
problem domain of metadata. @ is not clear to the uninitiated. @ is
counter-intuitively present before the docstring. @name('Value') is
can be used to store metadata, but lacks ease of use and symmetry.

Finally, @ does not appear to follow the tradition of Python. It is a
very specialized extension statement, and at least deserves a descriptive
identifier in the code like 'decorate'.

Please don't stay silent on this issue. Speak out!

We should really have a public vote. I doubt the @ sign will be approved
if 99% of Python users oppose it.

- Connelly
 
R

Rob Williscroft

wrote in in
comp.lang.python:
class foo:
def introduceNewFeature(self, someArgument, anotherArgument):
.decorate = [synchronized]
.accepts = (int, int)
.author = 'Chris King'
pass # whatever

AIUI it is intended that prefix '.' will be used at some point
with a 'with'/'using' keyword:

class Foo:
def f( self ):
with self:
.member = value

But since we can have function's in function's and we already
have "special" member functions (__init__ etc):

class Foo:

def method( self ):

def __decorate__( func ):
synchronized( func )

__accepts__ = (int, int)
__author__ = "whoever"

pass

Rob.
 
C

Christopher T King

wrote in in
comp.lang.python:
class foo:
def introduceNewFeature(self, someArgument, anotherArgument):
.decorate = [synchronized]
.accepts = (int, int)
.author = 'Chris King'
pass # whatever

AIUI it is intended that prefix '.' will be used at some point
with a 'with'/'using' keyword:

class Foo:
def f( self ):
with self:
.member = value

Which makes it all the more intuitive (def is acting similar to a with
block).
 
R

Rob Williscroft

Christopher T King wrote in @ccc6.wpi.edu in comp.lang.python:
wrote in in
comp.lang.python:
class foo:
def introduceNewFeature(self, someArgument, anotherArgument):
.decorate = [synchronized]
.accepts = (int, int)
.author = 'Chris King'
pass # whatever

AIUI it is intended that prefix '.' will be used at some point
with a 'with'/'using' keyword:

class Foo:
def f( self ):
with self:
.member = value

Which makes it all the more intuitive (def is acting similar to a with
block).

Intuitivly I would then expect .decorate to apply to self not to f.

But that's on a sample of 1 person's intuition :).

Rob.
 
H

Hallvard B Furuseth

barnesc said:
class foo:
def introduceNewFeature(self, someArgument, anotherArgument):
.decorate = [synchronized]
.accepts = (int, int)
.author = 'Chris King'
pass # whatever

I like it better than the current syntax, but I'm troubled by stuff
after the ':' which is executed before the function.
It _looks_ like it is executed inside the function.

Also, the scoping is a bit strange:

def bar(self, baz):
.decorate = [baz]
pass # whatever

The decorator looks like it refers to the function parameter, but it
doesn't: the parameter has no value when the decorator is set. Though I
guess a simple fix in this case is simply to forbid decorators to use
names that occur as function parameters.

Rob said:
But since we can have function's in function's and we already
have "special" member functions (__init__ etc):

class Foo:

def method( self ):

def __decorate__( func ):
synchronized( func )

__accepts__ = (int, int)
__author__ = "whoever"

pass

Same problem, only worse. The def statements of special member
functions are executed while the class body is executed, just like defs
of normal functions. OTOH, your special functions inside functions must
be executed once before the function is called.
 
A

Anthony Baxter

barnesc said:
class foo:
def introduceNewFeature(self, someArgument, anotherArgument):
.decorate = [synchronized]
.accepts = (int, int)
.author = 'Chris King'
pass # whatever

I like it better than the current syntax, but I'm troubled by stuff
after the ':' which is executed before the function.
It _looks_ like it is executed inside the function.

This was Guido's reason for rejecting all forms that put the decorator
syntax inside the function block. They're _not_ part of the function's
code.
 

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,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top