Decorator syntax

Christopher T King squirrel at WPI.EDU
Thu Aug 5 09:45:40 EDT 2004


On Thu, 5 Aug 2004, Paul McGuire wrote:

> remind me of when I lived in Maine. When I complained about how bad the
> drivers were, inevitably the response was "we're not as bad as the drivers
> in Massachusetts!"

I believe that statement is universally true ;)

I think much of the problem in coming up with a universally acceptable
decorator solution is that we're trying to kill too many birds with one
stone.  I believe the decorator problem can be seperated into at least
three distinct needs:


1) An easy, visually noticable way of declaring the type of a function, 
   such as classmethod and staticmethod.

This problem arose because classmethod and staticmethod weren't built 
in to the language.  The foo = classmethod(foo) syntax is a 
backwards-compatible hack, not the best way to do things.  For such a 
fundamental construct, we need language support.  Languages with support 
for these things already have a clear, consice way of specifying these 
things:

 def classmethod foo(self):
     pass

Why can't we do the same with Python?  The syntax doesn't have to 
support user constructs (indeed, it shouldn't), and it doesn't have to 
support more than one decorator (indeed, with only classmethod 
and staticmethod to choose from, this doesn't even make sense).


2) An easy, visually unobtrusive way of declaring metadata.

This is a problem we've encountered before.  The solution was to make a 
string at the top of the function behave as metadata, rather than code.  
Perhaps something similar can be done with a dictionary:

 def foo(a,b,c):
     {accepts: (int,int,list), author: 'Chris King'}
     pass

 foo.__metadata__['accepts'] --> (int,int,list)

Note that such a method is implementable in pure Python via some
byte-code trickery, and can thusly be made backwards-compatible.

Another (perhaps better) method of declaring metadata has been previously 
proposed, that of function attributes:

 def foo(a,b,c):
     .accepts = (int,int,list)
     .author = 'Chris King'

but this proposal has also (sadly) been previously rejected, despite its 
elegance and similarity to proposed "with" blocks.


3) An easy, visually noticable, yet unobtrusive way of mangling functions:

Doing such a thing, IMO, should not have syntax support.  When functions
are mangled in a user-defined way, it should look like this is happening.  
Why not make it convenient and wrap in up in a function:

 def foo(a,b,c):
     pass

 mutate(foo, make_my_function_awesome, partializer(5))

This has the advantage of being more general-purpose than syntax support:  
say make_my_function_awesome is not available, so a NameError is raised.  
The user can simply skip using that function in that case.  Or the
existence of make_my_function_awesome can be checked beforehand, and left
out of the mutate call if it's not available.

If syntax support is used, there's no way to try/except function manglers, 
save try/excepting the entire function defintion (and forcing multiple 
identical versions of the function to be defined).  Quite ugly.


So, to sum it up, let's look at what a totally pimped-out function might 
look like:

 def classmethod foo(self,a,b,c):
     """Returns a+b*c."""
     {accepts: (int,int,int), author: 'Chris King'}

     return a+b*c

 mutate(foo, make_my_function_awesome, partializer(5))

Compare to the current state of affairs:

 @partializer(5)
 @make_my_function_awesome
 @author('Chris King')
 @accepts(int,int,int)
 @classmethod
 def foo(self,a,b,c):
     """Returns a+b*c."""

      return a+b*c

Judge for yourself.




More information about the Python-list mailing list