Dynamically adding abstract methods in an abstract class

52 views Asked by At
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.

1

There are 1 answers

1
blhsing On BEST ANSWER

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