[Python-ideas] Anonymous functions for decorators

Ryan Freckleton ryan.freckleton at gmail.com
Wed Dec 26 00:33:31 CET 2007


I've been working with decorators to make a design by contract
library. This has gotten me thinking about decorators and anonymous
functions.

The two use cases I'll target are callbacks and decorating descriptors. i.e.
(from the twisted tutorial):

class FingerProtocol(basic.LineReceiver):
    def lineReceived(self, user):
        self.factory.getUser(user
        ).addErrback(lambda _: "Internal error in server"
        ).addCallback(lambda m:
                      (self.transport.write(m+"\r\n"),
                       self.transport.loseConnection()))

currently this can be modified to (ab)use a decorator:
    ...
    def lineReceived(self, user):
        @self.factory.getUser(user).addCallback
        def temp(_):
            return "Internal error in server"
    ...

This is an abuse of decorators because it doesn't actually decorate the
function temp, it registers it as a callback for the class FingerProtocol.

And this use of decorators, which I mimic for my design by contract
library. (Guido recently posted a patch on python-dev that would allow
the property to be used in the following way):

class C(object):
    x = property(doc="x coordinate")

    @x.setter
    def x(self, x):
        self.__x = x

    @x.getter
    def x(self):
        return self__x


There was some concern voiced on the list about repeating the variable
name both in the decorator and the function definition.

In other languages, these use cases are handled by anonymous functions
or blocks. Python also has an anonymous function, lambda, but it's
limited to a single expression. There are parsing and readability
issues with making lambda multi-line.

My proposal would be to use a special syntax, perhaps a keyword, to
allow decorators to take an anonymous function.

    @self.factory.getUser(user).addCallback
    def temp(_):
        return "Internal error in server"

    would become:

    @self.factory.getUser(user).addCallback
    do (_):
        return "Internal error in server"

    and:

    @x.setter
    def x(self, x):
        self.__x = x

    would become:
    @x.setter
    do (self, x):
        self.__x = x

In general:

@DECORATOR
do (..args...):
    ..function

would be the same as:

def temp(..args..):
    ..function
DECORATOR(temp)
del temp


The decorators should be stackable, as well so:

@DEC2
@DEC1
do (..args..)
    ..function

would be the same as:

def temp(..args..):
    ..function
DEC2(DEC1(temp))
del temp

Other uses/abuses

Perhaps this new 'do' block could be used to implement things like the
following:

@repeatUntilSuccess(times=10)
do ():
    print "Attempting to connect."
    socket.connect()

with repeatUntilSuccess implemented as:

def repeatUntilSuccess(times=None):
    if times is None:
        def repeater(func):
            while True:
                try:
                    func()
                    break
                except Exception:
                    continue
    else:
        def repeater(func):
            for i in range(times):
                try:
                    func()
                    break
                except Exception:
                    continue
    return repeater


@fork
do ():
    time.sleep(10)
    print "This is done in another thread."
    thing.dostuff()


with fork implemented as:

def fork(func):
    t = threading.Thread(target=func)
    t.start()

Open Issues:
~~~~~~~~~~~~
Doing a google search shows that the word 'do' is occasionally used as
a function/method name. Same for synonyms act and perform. Reusing def
would probably not be a good idea, because it could be easily confused
with a normal decorator and function.

This is still somewhat an abuse of the decorator syntax, it isn't
adding annotations to the anonymous function, so it isn't really
'decorating' it. I do think it's better than using a function that
returns None as a decorator.

While scanning source, the 'do' keyword may be easily confused with
def func. I'm not really sure what would work better, suggestions are
welcome. Perhaps I'm going down the wrong path by reusing decorator
syntax.

Other possible keywords instead of 'do': anonymous, function, act, perform.

I first thought of reusing the keyword lambda, but I think the
semantics of lambda are too different to be used in this case.
Similarly, the keyword with has different semantics, as well.

Thanks,

-- 
=====
--Ryan E. Freckleton



More information about the Python-ideas mailing list