4

I have the following code:

class Category(abc.ABC):
    foo = {"bar"}

Despite inheriting from abc.ABC, I can instantiate a Category just fine:

>>> a = Category()
>>> 

whereas I would expect it to raise an error like the following.

>>> class Foo(abc.ABC):
...     @abc.abstractmethod
...     def bar(self):
...         return "baz"
... 
>>> f = Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods bar
>>> 

It seems that this is because Category has no abstract methods. How can I ensure that trying to instantiate Category will raise an exception?

ash
  • 5,139
  • 2
  • 27
  • 39
  • Why do you need an exception? Is this just for understanding? – T. Arboreus Feb 27 '17 at 23:42
  • 1
    What you're doing seems odd to me, you may be going down a bad path. But if you're sure it's what you want, see this answer: http://stackoverflow.com/a/7990308/2337736 – Peter DeGlopper Feb 27 '17 at 23:52
  • Yes, this is a fairly bad way to approach it - I'm sure I'll rewrite this many times (it's a small personal project), so more as a point of interest than anything serious. – ash Feb 28 '17 at 00:08
  • So, an ABC is a way of defining an interface, but if it doesn't have any methods, it's not really an interface, eh? I guess I don't see the point. – wjandrea Apr 01 '23 at 15:10

1 Answers1

0

Here's one approach:

from abc import ABC, ABCMeta

class Category(ABC):
    foo = {"bar"}

    def __new__(cls, *args, **kwargs):
        if (cls.__base__ is ABC or cls.__class__ is ABCMeta):
            raise TypeError(f"Can't instantiate abstract class {cls}")
        return super().__new__(cls, *args, **kwargs)

This could also be helpful in case if you want to share this behavior between a few related abstract classes. For instance:

class CategoryGroup(ABC):
    baz = {"qux"}

    def __new__(cls, *args, **kwargs):
        if (cls.__base__ is ABC or cls.__class__ is ABCMeta):
            raise TypeError(f"Can't instantiate {cls.__name__}")
        return super().__new__(cls, *args, **kwargs)

class Category(CategoryGroup, ABC):
    foo = {"bar"}

In the example above, you would get a TypeError if you tried to create Category or CategoryGroup objects, as shown below:

>>> CategoryGroup()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
TypeError: Can't instantiate CategoryGroup
>>> Category()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
TypeError: Can't instantiate Category
>>> Category.__base__
<class '__main__.CategoryGroup'>
W D
  • 204
  • 1
  • 10
Archit
  • 976
  • 16
  • 28