tags:

views:

253

answers:

4

I am writing a class to implement an algorithm. This algorithm has three levels of complexity. It makes sense to me to implement the classes like this:

class level0:
    def calc_algorithm(self):
        # level 0 algorithm
        pass

    # more level0 stuff

class level1(level0):
    def calc_algorithm(self):
        # level 1 algorithm
        pass

    # more level1 stuff

class level2(level1):
    def calc_algorithm(self):
        # level 2 algorithm
        pass

    # more level2 stuff

Where I expect that calc_algorithm will be overridden in each class. Depending on a certain commandline option I want to run either level 0, level 1 or level 2 on the data. This is how I call the algorithm:

for offset in itertools.product(*map(xrange, (dim[0] - 1, dim[1] - 1, dim[2] - 1))):
    algorithm(offset).calc_algorithm

Where algorithm is either level0, level1 or level2.

The way I'd do it in other languages is:

for offset in itertools.product(*map(xrange, (dim[0] - 1, dim[1] - 1, dim[2] - 1))):
    if (level == 0):        
        level0(offset).calc_algorithm    
    else:
        if (level == 1):        
            level1(offset).calc_algorithm    
        else:
            level2(offset).calc_algorithm

Is there a Pythonic way to alias a class to refer to another class, so that I could do:

algorithm = (level == 0) and level0 or (level == 1) and level1 or level2

and then call algorithm as above?


Just for comparison, in Specman, which is an Aspect Oriented language I could write the class like this:

struct algorithm {
    level: uint;
    // common stuff for all levels

    calc_algorithm() is empty;

    when (level == 0) {
        calc_algorithm() is only {
            // level 0 algorithm
        };
    };
    when (level == 1) {
        calc_algorithm() is only {
            // level 1 algorithm
        };
    };
    when (level == 1) {
        calc_algorithm() is only {
            // level 1 algorithm
        };
    };

};

And then once I set the level struct member, I can use the rest of the class transparently.

+11  A: 

Are you looking for something along these lines?

dispatch = {0: level0, 1: level1, 2:level2}
dispatch[offset].calc_algorithm

Keys (and offset), obviously, could come from command line.

SilentGhost
It looks like your code assumes that `level0`, `level1`, and `level2` are *instances* of a class, when in fact they are classes that I want to instantiate.
Nathan Fellman
That should be no problem `dispatch[offset](..).calc_algorithm` (put your "constructor" arguments where the two periods are, or put nothing there.)
kaizer.se
+4  A: 
dispatch = {0:level0, 1:level1, 2:level2}
algo = dispatch[offset]()   # "calling" a class constructs an instance.
algo.calc_algorithm()

If you like introspection more:

class_name = "level%d" % offset
klass = globals()[class_name]
algo = klass()
algo.calc_algorithm()
Ned Batchelder
+2  A: 

The key is that — unlike in some other languages where classes are ‘special’ and you'd have to have some unusual means of ‘aliasing’ them — in Python, classes are themselves first-class objects which are referenced by perfectly normal variables.

So ‘a’ can be aliased as ‘b’ as simply as saying ‘b=a’.

Is there a Pythonic way to alias a class to refer to another class, so that I could do:

algorithm = (level == 0) and level0 or (level == 1) and level1 or level2

Well yes, that's exactly right and will already work just as you wrote it!

...although modern Python has the if/else expression so today you'd generally say:

algorithm= level0 if level==0 else level1 if level==1 else level2

but then a sequence access would probably be simpler than two conditionals:

algorithm= (level0, level1, level2)[level]
bobince
+1  A: 

Personally, I wouldn't create 3 classes, but one class with 3 different calculation methods, and dinamically change the official calculation method (the calculation interface) as needed. For example:

class algorithm:
    def __init__(self, level = 0):

     self.level = level
     self.calcFunctions = {0: self.calcLevel0, 1: self.calcLevel1, 2:self.calcLevel2}
     #initial value for calc function
     self.setLevel(level)

    def setLevel(self, newlevel):
     self.level = newlevel
     self.calc = self.calcFunctions[level]  

    def calcLevel0(self):
     """Level 0 calc algorithm"""
     #...
     pass

    def calcLevel1(self):
     """Level 1 calc algorithm"""
     #...
     pass

    def calcLevel2(self):
     """Level 2 calc algorithm"""
     #...
     pass


#class in use:

#level (taken from command line in your case)
level = 1

alg = algorithm()
alg.setLevel(level)
alg.calc()

If you don't need to change the calcFunction dinamically during execution, you could also pass level to class constructor

Mauro Bianchi
This is an elegant solution, and it's similar to what I considered initially, except that I would've had one `calc` function that had `if` statements to distinguish between the levels. The reason I prefer to have different classes is that each level requires various tables that don't need to be there for other levels. Having this all in the same file made it very messy. If they're in different classes they can be in different files as well, which makes the code more manageable.
Nathan Fellman
I understand your point. Anyway, you could set up different table loaders as well, based on the required level.
Mauro Bianchi
How can I do that?
Nathan Fellman
Different options, but what you exactly mean with "tables"?
Mauro Bianchi