Search code examples
pythondynamicabstract-classmetaclass

Dynamically adding abstract methods in an abstract class


from abc import ABCMeta, abstractmethod

class DynamicAbstractMeta(ABCMeta):
    def __new__(cls, name, bases, namespace):
        item_attributes = ["person", "animal"]

        # Create abstract methods based on Item attributes
        for attribute in item_attributes:
            namespace[attribute] = abstractmethod(lambda x, a=attribute: None)

        return super().__new__(cls, name, bases, namespace)

class A(metaclass=DynamicAbstractMeta):
    pass

class Item(A):
    def person(self):
        print("person")

    def animal(self):
        print("animal")

item_instance = Item()
item_instance.person()
item_instance.animal()

trackback:

item_instance = Item() TypeError: Can't instantiate abstract class Item with abstract methods animal, person

Why does it still throw an error even though I implemented the abstract methods in the Item class? Please help me.

Executes correctly without any errors.


Solution

  • The problem is that Item inherits A, so it also has the same metaclass of DynamicAbstractMeta, whose __new__ method would overwrite the person and animal methods defined in Item with abstract methods, making Item an abstract class.

    You can add a check in DynamicAbstractMeta.__new__ so that if the method is called for a subclass, identified by checking if any of the base classes has the same metaclass, it would not add any abstract methods to the namespace:

    class DynamicAbstractMeta(ABCMeta):
        def __new__(cls, name, bases, namespace):
            if cls not in map(type, bases): # add this check
                item_attributes = ["person", "animal"]
                for attribute in item_attributes:
                    namespace[attribute] = abstractmethod(lambda x, a=attribute: None)
            return super().__new__(cls, name, bases, namespace)
    

    Demo: https://ideone.com/JgG9CG