0

Suppose I'm writing a simple parser. It has a dispatcher, which calls the corresponding parsing functions depending on the type of the input expression.

def dispatcher(expression):
    m = pattern1.match(expression):
    if m is not None:
        handle_type1(expression, m)
    # ... other types

My question is, is there anyway to combine the matching and checking for None? I mean, something like the following C code:

void dispatcher(char *expression)
{
    if ((m = pattern1.match(expression)) != NULL) {
        // ... handle expression type 1
    }
    else if ((m = pattern2.match(expression)) != NULL) {
        // ... handle expression type 2
    }
    // ... other cases
}

Thanks!

pjhades
  • 1,948
  • 2
  • 19
  • 34
  • 2
    no, not really. Why do you want to do that? – SilentGhost Oct 29 '12 at 13:16
  • just look better, i also tried to put the compiled regex patterns into a tuple, and then go over it to avoid writing the if-else. – pjhades Oct 29 '12 at 13:19
  • uhm, have a look at `re.findall` then – SilentGhost Oct 29 '12 at 13:20
  • I asked a [very similar question](http://stackoverflow.com/questions/10359724/is-there-a-way-to-get-the-return-value-of-a-function-and-test-its-nonzero-at) a while back. The answer is *not without playing some dirty tricks* – mgilson Oct 29 '12 at 13:26
  • Duplicate of http://stackoverflow.com/questions/2554185/match-groups-in-python/2555047#2555047 – PaulMcG Oct 29 '12 at 14:33

4 Answers4

5

This isn't really about combining pattern matching with checking for none, it's about whether you can assign to a variable and evaluate the result of that assignment in one expression, because pattern.match() call could be any function returning a value.

And the answer in general is no, because in Python assignment is a statement, not an expression as it is in C.

The only difference I can see in this case is that you save yourself an extra carriage return, which isn't so useful. The assign-and-compare idiom is more useful in loops, but in Python you just have to do it over two lines (using break if necessary).

Kylotan
  • 18,290
  • 7
  • 46
  • 74
3

Don't you find your C-like example a bit repetitive? If you find you are repeating code over and over then consider replacing it with a loop. Something like this avoids duplicating the match and test:

def dispatcher(expression):
    for pattern, action in [
        (pattern1, handle_type1),
        (pattern2, handle_type2),
        ]:
        m = pattern.match(expression):
        if m is not None:
           action(expression, m)
           break

You can also pull the list of patterns and handlers out of the loop in various ways, for example if the handlers are all methods of the same class you could define a decorator to build the list automatically with the patterns defined beside the handlers.

@patternhandler('some regex')
def handle_type1(expression, match):
    ...
Duncan
  • 92,073
  • 11
  • 122
  • 156
2

The best you could do, and there's some debate whether this is better, is to create a class to do the work. Then the class can maintain state such as the last pattern matched:

class Matcher(object):
    def __init__(self):
        self.m = None

    def match(self, pattern, text):
        self.m = re.match(pattern, text)
        return self.m


def dispatcher(expression):
    matcher = Matcher()
    if matcher.match(pattern1, expression):
        handle_type1(expression, matcher.m)
    if matcher.match(pattern2, expression):
        handle_type2(expression, matcher.m)
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
1

Nowadays (since Python 3.8) the "walrus operator" := implements assignment expressions, allowing much simpler formulation:

if (m := pattern1.match(expression) is not None:

This combines evaluation and assignment in exactly the way you desire.

holdenweb
  • 33,305
  • 7
  • 57
  • 77