How to prevent a class from being instantiated in Python?

Asked

Viewed 202 times

3

I have a mother class and want to inherit it to a daughter class. But the mother class cannot be instantiated, only inherited. In other words, the mother class should be abstract.

Although Python’s POO does not support abstraction (yet), there is a standard library module called abc - having the class ABC and the function abstractmethod, to be used as a Decorator for methods of classes inherited from ABC.

Using the decorator abstractmethod in one of the class methods, it becomes abstract. However, my mother class should not have abstract methods. It should just be abstract.

The problem is that if I don’t declare some of the methods abstract, it can still be instantiated. See the code below that exemplifies what I am saying:

class Mother(ABC):
    def method(self): pass

class AbstractMother(ABC):
    @abstractmethod        # Não quero que esse método seja abstrato!
    def method(self): pass

obj = Mother()          # Compila, damn it...
obj2 = AbstractMother() # Não compila (como esperado)

A possible solution then to the problem would be to throw an exception in the method __new__ or in the __init__, to make a mistake whenever someone tries to instantiate directly from the class.

class Mother():
    def __init__(self): raise TypeError
    def method(self): pass

class Daughter(Mother):
    def __init__(self): pass

Daughter() # Compila
Mother()   # Não compilar, finalmente!

But this would be a "nut" solution, since all classes you inherit will be required to overwrite the method __new__ or the constructor, as in the code above.

That said, my doubt is: in Python 3, how to prevent a class from being instantiated, without forcing the child class to overwrite some method?

  • "The problem is that if I don’t declare some of the methods as abstract, it can still be instantiated" but whether a class cannot be instantiated, or it is Abstract or Static, if it is Abstract and has no Abstract method it is a class "without code" :) if you do not want to have an instance, use a class Static then

  • 4

    I will cause controversy here and only because I will use the term used in the question. I will comment even though I dislike some people passionate about certain technologies. If you want a solution not "nut" use a language not "nut". I explain. I’m not bad-mouthing Python. Some people know I’ve used Python in the past, I encouraged people to use it to do scripts and nobody listened to me. Today people use language for everything and nobody listens to me that is not to abuse it. People go for fashion, not because there’s a person talking.

  • 4

    Fashion is to wear without thinking about what you’re doing, use because others are wearing it. it’s not something fleeting as some think. Python is a good language for doing simple things. Python can be used for more complex things. This is not to say that it is suitable for this. So in certain scenarios it is the wrong tool for the task. Python is not a language of contracts, it was not made to give many guarantees at compile time, it tries to do what is possible at runtime.

  • 4

    So it is already a matter of profit not to compile when there is something abstract within the class. But language does not provide a way to have an abstract class without an abstract method. Which makes sense for a language that doesn’t want strong contracts. It is true that it also makes no sense to have for the method, so it is inconsistent. So the way the language let you do it is dirty, it’s hopeless. If you want something clean then you need to do it in a language that guarantees contracts all the time (which is rare, but for this case you do). Or accept language deficiency and use the "pig".

  • 1

    I don’t think the question called the language "pig," but okay, it might be a text interpretation problem for one of us.

  • 3

    @jsbueno yes, it seems that you are misinterpreting my text to suit your interest, as you always do. You cannot say that Python is not for all tasks that you appear wanting to sanctify the language. Interpreting my text for you: "if you want a clean solution, use a language that offers it, if you want a solution not ideal accept that the chosen language is not ideal and be happy".

  • 5

    The question does not call the language dirty and the comment does not say that. It seems to me that it is the "problem" of always, to say that Python is not a solution to all the problems of the universe. They say it can bother some people, even if it’s true.

  • Dear @Maniero I agree with your vision about Python, but this thread here in the comments is unnecessary (including this comment of mine is unnecessary). Check the help of this site: https://answall.com/help/privileges/comment

  • @Rafaelodon agree with you but as I participated in it if I delete will accuse me of wanting to hide the content. As has been done before, with your signage I think others might be able to do something. Just one detail: the first comments I made are pertinent to the question and add important information, and should not be deleted since they follow what determines the link you posted, the rest is only complaint because the person did not like the comments.

Show 4 more comments

2 answers

5

If for some reason you don’t want to put a simple comparison on __init__ how are you in reply from @jfaccioni , you can create a method __init_subclass__ in the base class that over-write an abstract method created only to prevent instantiation. (and use the abcs mechanisms)

The __init_subclass__ is a special class method that is called whenever a subclass is created.

import abc
class Base(abc.ABC):
    
    @abc.abstractmethod
    def _sentinel(self): pass
    
    def __init_subclass__(cls, *args, **kw):
        # Injeta um método só para sobre-escrever o abstract-method:
        cls._sentinel = lambda self: None

class B(Base): pass

In interactive mode:

In [10]: b = B()                                                                                                                                                                             

In [11]: a = Base()                                                                                                                                                                          
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-befb5e64d15f> in <module>
----> 1 a = Base()

TypeError: Can't instantiate abstract class Base with abstract method _sentinel

3


If I understand correctly, what you are looking for is to define the functionality of the method __init__ (or any other method) in the class Mother, but wants only the class Daughter can actually be instantiated. That is, "take advantage" of the Mother for the subclasses, but leave it as an "abstract soft class".

One way to do this is to check the class itself of the object being created within the __init__ and only produce the error if the class is Mother:

class Mother:
    def __init__(self, a, b, c):
        if self.__class__ is Mother:
            raise TypeError("Mother class cannot be instantiated")
        self.a = a
        self.b = b
        self.c = c

class Daughter(Mother):
    pass

Daughter(1, 2, 3)  # funciona
Mother(1, 2, 3)  # produz um TypeError

Although from a more theoretical point of view, I agree with the comment that it does not make much sense to have a class that is treated as if it possessed an abstract method, but which is defined within it.

  • 1

    this works. is a case where it is better to compare with "is" than with "==".

  • well observed @jsbueno, edited my example.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.