5

When defining an abstract metaclass in python and instantiating it like this:

from abc import ABC, abstractmethod


class AbstractMetaClass(type, ABC):
    @abstractmethod
    def func(self):
        pass


class MyClass(metaclass=AbstractMetaClass):
    pass

I would have expected my code to fail, since MyClass is an instance of an abstract class. Instead it runs with no problems. What is happening and how can I do this?

4
  • Can you clarify your use case here? Why do you need an abstract metaclass? Commented Jun 25, 2021 at 18:19
  • What are you trying to achieve? The abc module already provides the ABCMeta metaclass to create abstract classes. Commented Jun 25, 2021 at 18:21
  • 1
    I think the process of instantiating the metaclass bypasses the machinery that ABCMeta uses to enforce overriding of abstract methods. They just aren't designed to work together. Commented Jun 25, 2021 at 18:23
  • @chepner "not designed to work together" is the key phrase here. Commented Jun 25, 2021 at 18:39

1 Answer 1

7

Well, you simply found out it does not work. What you are thinking about makes sense: maybe it should fail. It is just that abstract classes are not designed to work as metaclasses, and work collaboratively with "type". I actually find incredible as most Python object mechanisms happen to "just work" when used with metaclasses - including properties, special dunder methods like __getitem__ and operator methods and so on. You just hit one thing that happened not to work.

If your design really makes sense, you may just want to manually make the check for abstract methods on your "abstract metaclass" __init__ method:

from abc import classmethod

class AbstractMetaClass(type):

    def __init__(cls, name, bases, ns, **kwargs):
        for meth_name, meth in cls.__class__.__dict__.items():
            if getattr(meth, "__isabstractmethod__", False):
                raise TypeError(f"Can't create new class {name} with no abstract classmethod {meth_name} redefined in the metaclass")
        return super().__init__(name, bases, ns, **kwargs)
        
    @abstractmethod
    def func(cls):
        pass

note that for clarity, it is better that ordinary methods on a metaclass have "cls" as the first argument rather than "self" (althought that might be a personal taste)

Sign up to request clarification or add additional context in comments.

1 Comment

One could even open an issue at bugs.python.org about this, but I have the feeling the request to make this work would be turned down.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.