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