Should a class constructor return a subclass?

Question:

Should a class constructor return a subclass?

This is mostly a question about OOP style and python style. I have problem where I need to implement a general case solution and, for performance reasons, I need to implement an optimized solution for a specific input type. The input type depends on the user. Currently I’ve implemented this by sub-classing the general case solution to make the optimized solution. I’ve come up with the following example to help describe what I mean.

from collections import Counter

class MyCounter(object):
    """General Case Counter"""
    def __init__(self, seq):
        self.seq = seq

    def count(self, key):
        return sum(key == item for item in self.seq)


class OptimizedCounter(MyCounter):
    """Counter optimized for hashable types"""
    def __init__(self, seq):
        self.counter = Counter(seq)

    def count(self, key):
        return self.counter.get(key, 0)

counter = MyCounter(['a', 'a', 'b', [], [0, 1]])
counter.count([0, 1])
# 1

counter = OptimizedCounter(['a', 'a', 'b'])
counter.count('a')
# 2

My question is how do I design a smooth interface so that the user gets an appropriate instance without having to worry about how it’s implemented. I’ve considered doing something like the following, but that feels ugly to me. Is there a more canonical or OOP way to do something like this?

class MyCounter(object):
    """General Case Counter"""
    def __new__(cls, seq):
        if hasOnlyHashables(seq):
            return object.__new__(OptimizedCounter)
        else:
            return object.__new__(MyCounter)
Asked By: Bi Rico

||

Answers:

Use a factory function that returns an instance of the appropriate class.

def makeCounter(seq):
    if hasOnlyHashables(seq):
        return OptimizedCounter(seq)
    else:
        return MyCounter(seq)
Answered By: BrenBarn

Your allocator implementation is a little off. If you need to create an instance of a child (or different) type then you do so by calling its constructor; only if you want to create an instance of the current class should you call the parent’s (object in this case) allocator.

No, the class constructor doesn’t return anything.

You need to create a factory as suggested by BrenBarn, but I would put that factory as a static method in the most generic class.

Something like this:

class MyCounter(object):
    @staticmethod
    def from_seq(seq):
        if hasOnlyHashables(seq):
            return OptimizedCounter(seq)
        else:
            return MyCounter(seq)

Answered By: stenci
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.