Paul McGuire <[EMAIL PROTECTED]> writes: > On May 19, 11:04 am, Arnaud Delobelle <[EMAIL PROTECTED]> wrote: >> Paul McGuire <[EMAIL PROTECTED]> 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 -- http://mail.python.org/mailman/listinfo/python-list