[Python-ideas] "While" suggestion

Terry Reedy tjreedy at udel.edu
Tue Jul 8 20:16:11 CEST 2008



Talin wrote:

> I'm actually more interested in the "as" clause being added to "if" 
> rather than "while". The specific use case I am thinking is matching 
> regular expressions, like so:
> 
>    def match_token(s):
>       # Assume we have regex's precompiled for the various tokens:
>       if re_comment.match(s) as m:
>          # Do something with m
>          process(TOK_COMMENT, m.group(1))
>       elif re_ident.match(s) as m:
>          # Do something with m
>          process(TOK_IDENT, m.group(1))
>       elif re_integer.match(s) as m:
>          # Do something with m
>          process(TOK_INT, m.group(1))
>       elif re_float.match(s) as m:
>          # Do something with m
>          process(TOK_COMMENT, m.group(1))
>       elif re_string.match(s) as m:
>          # Do something with m
>          process(TOK_STRING, m.group(1))
>       else:
>          raise InvalidTokenError(s)
> 
> In other words, the general design pattern is one where you have a 
> series of tests, where each test can return either None, or an object 
> that you want to examine further. 

One of the things I do not like above is the repetition of the 
assignment target, and statement ends, where it is a little hard to see 
if it is spelled the same each time.  I think it would be more 
productive to work on adding to the stdlib a standardized store-and-test 
class -- something like what others have posted on c.l.p.  In the below, 
I factored out the null test value(s) from the repeated if tests and 
added it to the pocket instance as a nulls test attribute.

def pocket():
   def __init__(self, nulls):
     if type(nul;s) != tuple or nulls == ():
         nulls = (nulls,)
     self.nulls = nulls
   def __call__(self, value):
     self.value = value
     return self
   def __bool__(self)
     return not (self.value in self.nulls)

then

     def match_token(s):
        # Assume we have regex's precompiled for the various tokens:
        p = pocket(None) # None from above, I don't know match return
        if p(re_comment.match(s)):
           # Do something with p.value
           process(TOK_COMMENT, p.value.group(1))
        elif p(re_ident.match(s)):
           # Do something with p.value
           process(TOK_IDENT, p.value.group(1))
        elif p(re_integer.match(s)):
           # etc

Pocket could be used for the while case, but I will not show that 
because I strongly feel 'for item in iteratot' is the proper idiom.

Your specific example is so regular that it also could be written 
(better in my opinion) as a for loop by separating what varies from what 
does not:

match_tok = (
   (re_comment, TOK_COMMENT),
   (re_indent, TOK_INDENT),
    ...
   (re_string, TOK_STRING),
)
# if <re_x> is actually re.compile(x), then tokens get bundled with
# matchers as they are compiled.  This strikes me as good.

for matcher,tok in match_tok:
   m = matcher.match(s)
   if m:
     process(tok, m.group(1)
     break
else:
   raise InvalidTokenError(s)

Notice how nicely Python's rather unique for-else clause handles the 
default case.

Terry Jan Reedy




More information about the Python-ideas mailing list