explain this function to me, lambda confusion

Arnaud Delobelle arnodel at googlemail.com
Mon May 19 17:27:06 EDT 2008


Paul McGuire <ptmcg at austin.rr.com> writes:

> 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.

I humbly think this is a very good use of decorators; it is one that I
frequently take advantage of and until I read this I had never thought
of it as deviant :).  After all, Python is not a functional language,
functions have side-effects and that's that.

In a way it is more basic than the 'typical' use, i.e.

    # Typical use; mutates defined function 
    @staticmethod
    def foo(bar, baz)...

is shorthand for

    def foo(bar, baz)...
    foo = staticmethod(foo)

Whereas

    # Deviant use; leaves function untouched
    @register
    def foo(bar, baz)...

is shorthand for

    def foo(bar, baz)...
    register(foo)

I am not claiming that it should be a common decorator idiom, only
that I am comfortable with it and find it useful.

> 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()

I am not a user of pyparsing (yet!), so my comment is completely
uninformed, but I feel that what is gained in brevity by writing it
like this, may be lost in clarity because separate notions are put
together (parsing, processing, debugging).  But if I understand
correctly, I would be able to rewrite this as:

# Grammar section
timestamp =  Regex(r"\d\d(\/\d\d\){2} \d\d(:\d\d){2}")

# Processing section
timestamp.setParseAction(convertTimeStamp)
timestamp.leaveWhitespace() # I'm not sure what this does!

# Debugging section
timestamp.setDebug()

OK, now I understand what my problem is: 

    - your existing setXXX methods mutate self and return it, thus
      breaking the quasi-rule that if you mutate something, don't
      return it (c.f. list.sort, etc);

    - your proposed 'decorator-friendly' setXXX methods mutate self
      and return their argument just to satisfy the
      decorator-friendliness constraint, but in spirit they return
      nothing.

This is just a train of thought, and I hope that you won't take this
the wrong way.  I am arguing for the pleasure of it, and I am happy to
lose the argument :)
 
> 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

You're (probably!) the best person to judge the syntactical balance of
pyparsing!

> 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.

I understand your comment about list-comprehensions, but I haven't yet
reached the level of Python guru-ness to correlate it to decorators :)

There are more things I would have liked to expand on but
unfortunately I don't have enough time to put my thoughts together in
a communicable manner.

-- 
Arnaud



More information about the Python-list mailing list