Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual classes: doing it right?

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.

like image 828
xApple Avatar asked Jun 19 '10 17:06

xApple


1 Answers

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
                new_cls = Line
            else:
                raise ValueError("Invalid description: {}.".format(description))
        else:
            new_cls = cls
        return super(Shape, cls).__new__(new_cls, *args, **kwargs)

    def number_of_edges(self):
        return "A shape can have many edges…"

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

class SomeShape(Shape):
    pass

>>> l1 = Shape("It's flat")
>>> l1.number_of_edges()
1
>>> l2 = Line()
>>> l2.number_of_edges()
1
>>> u = SomeShape()
>>> u.number_of_edges()
'A shape can have many edges…'
>>> s = Shape("Hexagon")
ValueError: Invalid description: Hexagon.
like image 140
Georg Schölly Avatar answered Nov 01 '22 13:11

Georg Schölly