Home > Software engineering >  Why we declare metaclass=abc.ABCMeta when use abstract class in python?
Why we declare metaclass=abc.ABCMeta when use abstract class in python?

Time:04-18

When I was reading the code online, I have encountered the following cases of using abstract classes:

from abc import abstractmethod,ABCMeta
class Generator(object,metaclass=ABCMeta):
    @abstractmethod
    def generate(self):
        raise NotImplementedError("method not implemented")
generator=Generator()
generator.generate()

The following error is returned, as expected:

TypeError: Can't instantiate abstract class Generator with abstract methods generate

But if I write it like this (the only difference is in the second line)

from abc import abstractmethod,ABCMeta
class Generator(object):
    @abstractmethod
    def generate(self):
        raise NotImplementedError("method not implemented")
generator=Generator()
generator.generate()

Although there are changes in the error message,

NotImplementedError: method not implemented

When I implemented the generate method, both of the above ways of Generator were executed correctly,

class GeneticAlgorithm(Generator):
    def generate(self):
        print("ABC")
ga=GeneticAlgorithm()
ga.generate()
>>> ABC

So why do we need the statement metaclass=ABCMeta?

I know something from GeeksforGeeks that

ABCMeta metaclass provides a method called register method that can be invoked by its instance. By using this register method, any abstract base class can become an ancestor of any arbitrary concrete class.

But this still doesn't make me understand the necessity of declaring metaclass=ABCMeta, it feels like @abstractmethod modifying the method is enough.

CodePudding user response:

The second example,

from abc import abstractmethod,ABCMeta
class Generator(object):
    @abstractmethod
    def generate(self):
        raise NotImplementedError("method not implemented")
generator=Generator()
generator.generate()  # HERE it raises

does not use the @abstractclass decorator in any way. It only raises the NotImplemetedError exception when the generate() function is called, whereas in the first example an error is raised on the instantiation (generator=Generator()).

In order to use @abstractmethod, the class has to have metaclass ABCMeta (or it has to inherit from such class, e.g. ABC).

By the way, there are ways to check whether a method is implemented even sooner (on class definition) using, for example __init_subclass__ or by defining a custom metaclass and modifying its __new__ method.

CodePudding user response:

You "need" the metaclass=ABCMeta to enforce the rules at instantiation time.

generator=Generator()  # Errors immediately when using ABCMeta
generator.generate()   # Only errors if and when you call generate otherwise

Imagine if the class had several abstract methods, only some of which were implemented in a child. It might work for quite a while, and only error when you got around to calling an unimplemented method. Failing eagerly before you rely on the ABC is generally a good thing, in the same way it's usually better for a function to raise an exception rather than just returning None to indicate failure; you want to know as soon as things are wrong, not get a weird error later without knowing the ultimate cause of the error.

Side-note: There's a much more succinct way to be an ABC than explicitly using the metaclass=ABCMeta syntax:

from abc import abstractmethod, ABC

class Generator(ABC):

Python almost always makes empty base classes that use the metaclass to simplify use (especially during the 2 to 3 transition period, where there was no compatible metaclass syntax that worked in both, and direct inheritance was the other thing that worked).

  • Related