Virtual classes: doing it right?

11,715

Solution 1

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):
    classDict = {"It's flat":Line("It's flat"), "It's spiky":Triangle("It's spiky"), "It's big":Rectangle("It's big")}
    return classDict[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

Solution 2

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.

Solution 3

Python doesn't have virtual classes out of the box. You will have to implement them yourself (it should be possible, Python's reflection capabilities should be powerful enough to let you do this).

However, if you need virtual classes, then why don't you just use a programming language which does have virtual classes like Beta, gBeta or Newspeak? (BTW: are there any others?)

In this particular case, though, I don't really see how virtual classes would simplify your solution, at least not in the example you have given. Maybe you could elaborate why you think you need virtual classes?

Don't get me wrong: I like virtual classes, but the fact that only three languages have ever implemented them, only one of those three is still alive and exactly 0 of those three are actually used by anybody is somewhat telling …

Share:
11,715
xApple
Author by

xApple

Updated on June 08, 2022

Comments

  • xApple
    xApple almost 2 years

    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.

  • TooAngel
    TooAngel almost 14 years
    nice - I also thought about something like that, but didn't know the python syntax for it
  • Stefan
    Stefan almost 14 years
    @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.
  • xApple
    xApple about 13 years
    I implemented this solution and discovered that is suffers from a flaw: the init method of the class Shape will be called twice when creating an instance.
  • xApple
    xApple about 13 years
    I can confirm this solution is not appropriate. To fix it see stackoverflow.com/questions/5953759.
  • Jörg W Mittag
    Jörg W Mittag about 8 years
    Interesting! I didn't know that. Do you have a link where I can read up on virtual classes in C++? According to Wikipedia, in C++, nested classes are static members of the enclosing class. In other words: they aren't actually true nested classes (which are members of an enclosing instance of the enclosing class). And considering that virtual classes are a special case of true nested classes (namely true nested classes which be overridden in a subclass just like any other virtual member), this seems to preclude C++ from having virtual classes.
  • user1717828
    user1717828 about 8 years
    Mmm, AFAIK C++ sort of defines what virtual classes are, because the language is so popular and make such heavy use of them. A virtual/abstract class cannot be instantiated, only inherited, so in a way it's more like a template for other classes to feed off of.
  • Jörg W Mittag
    Jörg W Mittag about 8 years
    I'm not sure what you mean. The term virtual class is well-defined and has a precise meaning that is unrelated to C++ or any other language in particular. What you describe has absolutely nothing to do with virtual classes. What you describe is an abstract base class. Google has exactly 0 hits for virtual classes in C++. There are virtual base classes in C++, which are related to virtual inheritance, but none of them are related to virtual classes. Like I said: a virtual class is a class that is a member of an instance and can be overridden in a subclass, just like a virtual method …
  • Jörg W Mittag
    Jörg W Mittag about 8 years
    … is a member of an instance and can be overridden in a subclass. In fact, in a language like Python, where classes are first-class objects, you can implement virtual classes by using methods which return classes. I believe you when you say that C++ has virtual classes, I don't know enough about C++ to judge that, but neither your example nor your link show virtual classes in C++, there are no Google hits about virtual classes in C++, and the Wikipedia article on C++ says that nested classes are static members, which conflicts with virtual classes where nested classes are non-static members.
  • user1717828
    user1717828 about 8 years
    It appears you're right; I never knew I was mixing up those two concepts. Thanks for the catch.
  • Jim Oldfield
    Jim Oldfield over 6 years
    Your second factory function constructs an object of every type every time it's called, even though you only return one of them. Better to store classes in the dictionary than objects: class_dict = {"It's flat": Line, ...} then return class_dict[description](description).