views:

110

answers:

4

I have been reading documentation describing class inheritance, abstract base classes and even python interfaces. But nothing seams to be exactly what I want. Namely, a simple way of building virtual classes. When the virtual class gets called, I would like it to instantiate some more specific class based on what the parameters it is given and hand that back the calling function. For now I have a summary way of rerouting calls to the virtual class down to the underlying class.

The idea is the following:

class Shape:
    def __init__(self, description):
        if   description == "It's flat":  self.underlying_class = Line(description)
        elif description == "It's spiky": self.underlying_class = Triangle(description)
        elif description == "It's big":   self.underlying_class = Rectangle(description)
    def number_of_edges(self, parameters):
        return self.underlying_class(parameters)

class Line:
    def __init__(self, description):
        self.desc = description
    def number_of_edges(self, parameters):
        return 1

class Triangle:
    def __init__(self, description):
        self.desc = description
    def number_of_edges(self, parameters):
        return 3

class Rectangle:
    def __init__(self, description):
        self.desc = description
    def number_of_edges(self, parameters):
        return 4

shape_dont_know_what_it_is = Shape("It's big")
shape_dont_know_what_it_is.number_of_edges(parameters)

My rerouting is far from optimal, as only calls to the number_of_edges() function get passed on. Adding something like this to Shape doesn't seam to do the trick either:

def __getattr__(self, *args):
    return underlying_class.__getattr__(*args)

What I am doing wrong ? Is the whole idea badly implemented ? Any help greatly appreciated.

+6  A: 

I would prefer doing it with a factory:

def factory(description):
    if   description == "It's flat":  return Line(description)
    elif description == "It's spiky": return Triangle(description)
    elif description == "It's big":   return Rectangle(description)

or:

def factory(description):
    class = {"It's flat":Line("It's flat"), "It's spiky":Triable("It's spiky"), "It's big":Rectangle("It's big")}
    return class[description]

and inherit the classes from Shape

class Line(Shape):
    def __init__(self, description):
        self.desc = description
    def number_of_edges(self, parameters):
        return 1
TooAngel
A: 

You can change the class with object.__class__, but it's much better to just make a function that returns an instance of an arbitrary class.

On another note, all class should inherit from object unless you use using Python 3, like this, otherwise you end up with an old-style class:

class A(object):
    pass
Longpoke
+1  A: 
Jörg W Mittag
+4  A: 

I agree with TooAngel, but I'd use the __new__ method.

class Shape(object):
    def __new__(cls, *args, **kwargs):
        if cls is Shape:                            # <-- required because Line's
            description, args = args[0], args[1:]   #     __new__ method is the
            if description == "It's flat":          #     same as Shape's
                instance = Line(*args, **kwargs);
        else:
            instance = super(Shape, cls).__new__(cls, *args, **kwargs)
        return instance

    def number_of_edges(self):
        return 0

class Line(Shape):
    def number_of_edges(self):
        return 1

instance = Shape("It's flat")
instance.number_of_edges()
-> 1
Georg
nice - I also thought about something like that, but didn't know the python syntax for it
TooAngel
In this example, Shape is known as a _metaclass_.
Daniel Newby
@Daniel: I don't think so. Metaclasses usually change the way a class works. In Objective-C this would be called a *Class Cluster*. I'm not sure what the proper name for it is in Python.
Georg
@Georg: You are right.
Daniel Newby