[Python-Dev] PEP318 and @decorators

Trevor Blackwell tlb at tlb.org
Mon Nov 22 23:11:23 CET 2004


I just read the plan for decorators in Python 2.4, and I'm surprised to
see no discussion of what seems to me the cleanest answer, to optionally
write function definitions as assignments:

   foo=def(a,b):
       print a,b

which allows trivial modification by decorators:

   bar1=staticmethod(def(a, b)):
       print a,b

   bar2=accepts(int, int, staticmethod(def(a, b))):
       print a,b

   bar3=adddocstring("this function prints its arguments", 
                     trace(def(a,b))):
       print a,b

It makes it clear in which order the functions are called, unlike with
the @ syntax, and what their arguments are. It has obvious line-break
syntax. You can include arguments to the functions without having to
curry them, making it much easier to write one's own decorator
functions.

Both the "def foo(...):" and "foo=def(...):" syntaxes would be accepted.
I don't think there's any ambiguity. 

Classes would work the same way:

   SimpleClass=class(object):
       ...

   FancyClass=tweakclass(class(object)):
       ...

Among other things, by unifying assignment and definition, it makes the
semantics of function name (re-)binding obvious, so if you read code
like,

   foo=def(a,b):
       print "goodbye"

   foo2=foo

   foo=def(c,b):
       print "hello"

   foo()
   foo2()

it's obvious what will happen, while with the current syntax people
familiar with other languages would expect an error or undefined
behavior. It also works nicely for nested functions, making it obvious
that it binds in the local namespace just like regular assignment.
Making function definition just another sort of assignment emphasizes
the dynamic nature of the language, and will encourage people to treat
functions as first-class data types.

The precise rule is that when there is a def or class in a line (and
there can be only one) there must be a colon and a block following.

You could even use this to provide a more general alternative to lambda,
allowing multi-statement lambdas:

   x=map(def(a), [1,2,3]):
       b=sin(a)
       return b*3
   assert x == [3*sin(1), 3*sin(2), 3*sin(3)]

though this might not be something to encourage. And it makes stashing
functions somewhere easy:

   atexit.register(def()):
       print "exiting..."

   converters.append(def(a)):
       ... convert something ...

Etags doesn't currently grok this syntax, but it should anyway because
it would also help tag global variables. It's easy to implement with a
regex, something like /^\s*(\w+)\s*=.*\bdef\b/

One bummer is that it may be more difficult to set the __name__ property
of a function.

-- 
Trevor Blackwell      tlb at tlb.org          (650) 776-7870




More information about the Python-Dev mailing list