explain this function to me, lambda confusion

P

Paul McGuire

[...]

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!

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
 
A

Arnaud Delobelle

Paul McGuire said:
[...]

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!

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

Bruno Desthuilliers

Paul McGuire a écrit :
[...]

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!

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

You just have to provide three decorators instead, one for each callback.
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.

This is already a well-known decorator pattern. It's used in CherryPy to
mark methods that are exposed as request handlers.
 
A

Arnaud Delobelle

Bruno Desthuilliers said:
Paul McGuire a écrit :
[...]

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!

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

You just have to provide three decorators instead, one for each callback.
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.

This is already a well-known decorator pattern. It's used in CherryPy
to mark methods that are exposed as request handlers.

Actually, IIRC the decorator provided by CherryPy is something like
(names are probably wrong, as I haven't looked at CherryPy for a
while):

def exposed(f):
f.is_exposed = True
return f

So it doesn't mutate anything else than the function that it
decorates, but changes slightly how the world sees that function.
This is in line with classic decorators such as staticmethod,
classmethod, etc.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,008
Latest member
HaroldDark

Latest Threads

Top