explain this function to me, lambda confusion

Paul McGuire ptmcg at austin.rr.com
Mon May 19 15:28:04 EDT 2008


On May 19, 11:04 am, Arnaud Delobelle <arno... at googlemail.com> wrote:
> Paul McGuire <pt... at austin.rr.com> writes:
>
> [...]
>
> Could you use it as a decoratore instead?
>
> integer = Word("0123456789")
>
> @integer.setParseAction
> def parse_integer(tokens):
>     return int(tokens[0])
>
> I could make your grammar clearer, because you don't mix it with
> processing code... and no need for lambdas!
>
> --
> Arnaud

What a sexy little idiom!  You could really apply this to any API
method that accepts a callable as a single argument, and pyparsing
actually has several of these:

setParseAction
addParseAction
setFailAction
setDebugActions

(Unfortunately, setDebugActions requires 3 callables for its arguments
- one to be run when an expression is about to be parsed, one to be
run after parsing is complete, and one to be run if the expression
fails to be parsed.  So setDebugActions can't be used in this
decorator manner.)

Using these methods as decorators deviates from the typical decorator
usage model as I understand it - instead of wrapping the provided
function within some enclosing setup/teardown code (like lock/unlock,
or open-file/close-file, or begin-transaction/commit-transaction), and
then returning the created wrapper function, the decorator usage you
propose is one in which the decorator uses the provided function with
some side-effect (such as setting a property), but then just returns
the original function.  By returning the original function, we could
stack decorators so that multiple expressions could share the same
parse action:

@articleTitle.setParseAction
@movieTitle.setParseAction
@bookTitle.setParseAction
def upcase_title(tokens):
    return " ".join( t.title() for t in tokens )

Here is where I have something of a hitch, with the way pyparsing
implements most setXXX methods.  setParseAction already returns a
value, and the value returned is self.  If you have any Smalltalk
background, this will seem familiar to you.  (Not that I was ever a
big-time Smalltalk coder, but this was one language idiom that you
learned on Day 0.5 or you were lost forever.)  This makes it easy to
chain together a constructor and multiple property setters into a
single expression:

timestamp = Regex(r"\d\d(\/\d\d\){2} \d\d(:\d\d)
{2}").setParseAction(convertTimeStamp).leaveWhitespace().setDebug()

In the case where we have a single parse action shared by multiple
expressions, we have to fall back to:

def upcase_title(tokens):
    return " ".join( t.title() for t in tokens )
articleTitle.setParseAction(upcase_title)
movieTitle.setParseAction(upcase_title)
bookTitle.setParseAction(upcase_title)

But, now that I've looked at this for a while, I may fall back on some
other idioms:
- just because you *can* do something doesn't mean you *should* do it
- explicit is better than implicit

Decorator syntax is already a mysterious topic for many newbies, even
when used for its normal application.  Using a decorator to perform
the same function as an explicit "set" call invokes cleverness at the
cost of clarity.  Using decorators to replace:

def methodX(a,b,c):
    blah
methodX = staticmethod(methodX)

with

@staticmethod
def methodX(a,b,c):
    blah

does have some merits, including DRY.  But using decorator syntax as
an implicit invocation of a set method? It's just taking advantage of
the incidental implementation of the decorator syntax. It would be
like implementing the logic of a for-loop using a list comprehension -
clever, and yes it can be done, but maybe a bit obscure.

-- Paul



More information about the Python-list mailing list