Python: callbacks and mix-in class

What's the problem?python

Assuming that you have some well written library which parses a file and invokes callbacks when some certain pattern's encountered (For example, SAX).ide

The most common way to make use of its utility is to inherit from it, override the callbacks and do your own stuff.this

Here's a small piece of code demonstrating this method:spa

import re, sys
class Substantial:
    regexp_callback = re.compile(r'<([^<>]+)>')
    def callback(self, name=None):
        print 'Substantial callback'
    def parse(self, string):
        m = self.regexp_callback.match(string)
        if m:
            self.callback(m.group(1))

class MyHandler(Substantial):
    def callback(self, name=None):
        print 'MyHandler callback'
        # we're likely to have a long-long if-else statement here     
        # without mixin class

As the code comment states, WE ARE LIKELY TO HAVE A LONG IF-ELSE STATEMENT IN CALLBACKS.scala

Assuming the input file has contents like:code

<callback1>regexp

<hello>xml

<nice>ip

...ci

It's probable that we've got to write code in callbacks like this:

if content-in-angle-brackets == 'callback1': XXXX

elif content-in-angle-brackets == 'hello': XXXX

.....

This is not scalable and difficult to maintain!

How do we solve this problem, i.e, avoid the long if-else statement to make the project more maintainable?

First Solution to Avoid Long If-Else problem

The first solution may be obvious. Most of us may code like this:

class MyHandler(Substantial):
    def callback(self, name=None):
        # we're likely to have a long-long if-else statement here                                                                                                                                               
        # without mixin class                                                                                                                                                                                   
        if name.upper() == 'CALLBACK1':
            self.doCALLBACK1()
        elif name.upper() == 'CALLBACK2':
            self.doCALLBACK2()
        elif name.upper() == 'CALLBACK3':
            self.doCALLBACK3()
    # actual callbacks                                                                                                                                                                                          
    def doCALLBACK1(self):
        print 'MyHandler: doCALLBACK1'
    def doCALLBACK2(self):
        print 'MyHandler: doCALLBACK2'
    def doCALLBACK3(self):
        print 'MyHandler: doCALLBACK3'

def parseFile(filename, handler):
    with open(filename, "r") as f:
        for line in f:
            handler.parse(line.strip())

Well, this is acceptable, but the long if-else problem's still there. If We're going to define 10 callbacks, we're forced to write 10 if-elif statements. Isn't there any better way? Say, we just write a small piece of code, and this code takes care of dispatching methods, and the only thing we should focus on is to write well-defined callbacks.

A Better Solution: mix-in class (used as a dispatcher and helper)

The solution is that by introducing a mix-in class, we delegate the dispatching task to the mix-in class and only focus on actual callback functions.

The code is like this.

class Mixin:
    def callback(self, name=None):
        if name:
            mname = 'do'+name.upper()
            method = getattr(self, mname, None)
            if callable(method):
                method()

class MyHandler2(Mixin, Substantial):
    # actual callbacks                                                                                                                                                                                          
    def doCALLBACK1(self):
        print 'MyHandler2: doCALLBACK1'
    def doCALLBACK2(self):
        print 'MyHandler2: doCALLBACK2'
    def doCALLBACK3(self):
        print 'MyHandler2: doCALLBACK3'

Demo

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print 'argument error, please specify an input file'
    else:
        print '=======Substantial================'
        parseFile(sys.argv[1], Substantial())
        print '=======MyHandler(Directly inherit from Substantial=========='
        parseFile(sys.argv[1], MyHandler())
        print '=======MyHandler2 (Inherit from Mixin and Substantial, Mixin as a dispather or helper) =='
        parseFile(sys.argv[1], MyHandler2())


input file contents:

<hello>
world
{callBack1}
{callBack2}
<callBack1>
<callBack2>
hello
world
<callback3>
<mycallback>

Result

chenqi@chenqi-OptiPlex-760:~/mypro/python/xml-parser$ ./test.py input =======Substantial================ Substantial callback Substantial callback Substantial callback Substantial callback Substantial callback =======MyHandler(Directly inherit from Substantial========== MyHandler: doCALLBACK1 MyHandler: doCALLBACK2 MyHandler: doCALLBACK3 =======MyHandler2 (Inherit from Mixin and Substantial, Mixin as a dispather or helper) == MyHandler2: doCALLBACK1 MyHandler2: doCALLBACK2 MyHandler2: doCALLBACK3 chenqi@chenqi-OptiPlex-760:~/mypro/python/xml-parser$

相關文章
相關標籤/搜索