user2177304
user2177304

Reputation:

Class that should not be instantiated

I want to create a class hierarchy in which I have a class Block which can be instantiated by itself. Then I have a class List which inherits from Block and contains methods common to all lists, and finally I have classes OrderedList, LableledList etc that inherit from List. I want people to be able to instantiate OrderedList etc, but not List.

In other words, you can instantiate a plain Block and you can instantiate an OrderedList that inherits from List that inherits from Block, but you can't instantiate List.

All attempts to Google this lead to Abstract Base Classes, but none provides and example that fits this case and I am having trouble extrapolating.

Upvotes: 5

Views: 3487

Answers (4)

Noctis Skytower
Noctis Skytower

Reputation: 21991

The following conversation with the interpreter should show how this is possible. After inheriting from the Abstract Base Class with Block, you only need to mark the initializer on List as being an abstractmethod. This will prevent instantiation of the class without causing problems for child classes.

>>> import abc
>>> class Block(abc.ABC):
    def __init__(self, data):
        self.data = data


>>> class List(Block):
    @abc.abstractmethod
    def __init__(self, data, extra):
        super().__init__(data)
        self.extra = extra


>>> class OrderedList(List):
    def __init__(self, data, extra, final):
        super().__init__(data, extra)
        self.final = final


>>> instance = Block(None)
>>> instance = List(None, None)
Traceback (most recent call last):
  File "<pyshell#42>", line 1, in <module>
    instance = List(None, None)
TypeError: Can't instantiate abstract class List with abstract methods __init__
>>> instance = OrderedList(None, None, None)
>>> 

Upvotes: 4

jsbueno
jsbueno

Reputation: 110261

The "one and obvious" way to do this is to use ABCMeta and mark some methods as abstract as documented on other answers.

But if in your case you don't have a set of methods that one has to override in a mandatory way (let's suppose your __init__ is reusable in some cases, and other of the list methods as well):

In that case you can create a __new__ method that checks if the clas being istantiated is the own class, and raises. To do that, you have to use teh magic __class__ variable that is documentend only in corners of Python docs - if you as much as use the __class__ variable in any method body, it will automatically take the value of the class where it was declared, at run time. It is part of the parameterless super mechanism of Python 3.

Thus:

class List(Block):
    def __new__(cls, *args, **kw):
        if cls is __class__:
            raise TypeError(cls.__name__ + " can't be directly instantiated")
        return super().__new__(cls, *args, **kw)

Btw, you should give preference for the ABCMeta abstractmethods if your pattern allows it. Note that if your classes use a custom metaclass it will conflict with the ABCMeta as well - so you may need to resort to this as well

(If you don't further customize __new__, then you'd better not pass args and kw upstream on the __new__ method: Python's object.__new__ ignore extra args if __init__ is defined but __new__ is not in the subclasses - but if both are defined it raises an error)

Upvotes: 0

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160397

Inherit from ABC located in the abc module and make methods that are implemented in base classes (that inherit from List) @abstractmethods (a decorator located in abc):

from abc import ABC, abstractmethod

class List(ABC, Block):

    @abstractmethod
    def size(self):
        return 0 

Having an ABC with @abstractmethods defined forbids from instantiation.

Upvotes: 0

Delly Fofie
Delly Fofie

Reputation: 314

Your List class should have ABCMeta as a metaclass and make the init methods abstract.

from abc import ABCMeta
class List(metaclass=ABCMeta):
    @abstractmethod
    __init__():
        pass

https://docs.python.org/3/library/abc.html

Upvotes: 0

Related Questions