[Tutor] a class that may not be instantiated

Steven D'Aprano steve at pearwood.info
Tue Nov 24 13:19:57 EST 2015


On Tue, Nov 24, 2015 at 03:36:21PM +0000, Albert-Jan Roskam wrote:
> Hi,
> 
> I have two classes with a number of methods that are the same, so I 
> want to define a super class that holds these methods.

Sounds like a misuse of classes to me. DRY is not a good reason to make 
two otherwise unrelated classes subclasses of an otherwise pointless 
parent.

Better solutions include:

- Factor out the common code to a module-level function, and have the 
classes call that function from their methods:

def spam(n):
    return " ".join(["spam"]*n)

class Parrot:
    def speak(self):
        return "Polly want some %s." % spam(5)

class Viking:
    def sing(self):
        return "%s WONDERFUL %s!!!" % (spam(4), spam(1))



- Use a Mixin class to hold the shared methods, and "mix them in" to the 
two other classes as needed.


In Python, mixins aren't treated any differently from "ordinary" 
classes. But the difference is conceptual. Contrast:

class Generic: ...
class Parrot(Generic): ...
class Viking(Generic): ...

This implies that both parrots and vikings are a kind of "generic", 
whatever that is.

class SpamMixin: ...
class Parrot(SpamMixin): ...
class Viking(SpamMixin): ...

This tells the reader that the intent is for the SpamMixin to "mix in" 
common methods to parrots and vikings, without implying anything about 
them both being the same kind of thing.


But, let's say you still want to proceed with your first plan:


> But the super 
> class (below called _Generic) should not be instantiated, because it 
> serves no purpose other than the DRY principle. I raise a 
> NotImplementedError in case if somebody dares to instantiate _Generic.

Sounds reasonable, although you should be kind to your subclasses:

class _Generic(object):
    def __init__(self, *args, **kwargs):
        if type(self) is _Generic:
            raise NotImplementedError('abstract base class cannot be instantiated')


Now your concrete subclasses aren't forced to override __init__ if they 
don't need to.



-- 
Steve


More information about the Tutor mailing list