Search code examples
pythonpython-3.xoopmetaprogramming

Dynamically created classes with the same name


I'm trying to dynamically create subclasses in Python with type:

class A:
    @classmethod
    def create_subclass(cls, name, attrs):
        return type(name, (cls,), attrs)

for i in range(5):
    x = A.create_subclass("B", {"func": abs})

print(A.__subclasses__())

and here's what I see in the output:

[<class '__main__.B'>, <class '__main__.B'>, <class '__main__.B'>, <class '__main__.B'>, <class '__main__.B'>]

Obviously, this was not my intention. Two questions in that respect:

  1. How does Python handles multiple classes with identical names?
  2. What is a pythonic way to handle it? Of course, I can look up the name in the already existing subclasses, but then how to deal with namespaces/modules?

Solution

  • Python doesn't care overmuch about the classname, these will be different classes, you just need to have a mechanism to save and look up the classes you want.

    class A:
    
        def __repr__(self):
            return f"{self.__class__.__name__} floor={self.floor}"
    
    
        @classmethod
        def create_subclass(cls, name, attrs):
            return type(name, (cls,), attrs)
    
    di_cls = {}
    #save them in a dictionary
    for i in range(5):
        di_cls[i] = A.create_subclass("B", {"func": abs, "floor" : i})
    
    for i, cls in di_cls.items():
        print(f"{cls()}")
    
    class config:
        #put them in hierarchical namespaces
        cls_level1 = A.create_subclass("B", {"func": abs, "floor" : 11})
    
        class level2:
            cls_level2 = A.create_subclass("B", {"func": abs, "floor" : 22})
    
    print(f"{config.cls_level1()=}")
    print(f"{config.level2.cls_level2()}")
    
    l1 = config.cls_level1()
    l2 = config.level2.cls_level2()
    
    print(f"{isinstance(l1, A)=}")
    print(f"{isinstance(l2, A)=}")
    print(f"{isinstance(l2, config.level2.cls_level2)=}")
    print(f"{isinstance(l2, config.cls_level1)=}")
    
    
    

    output:

    B floor=0
    B floor=1
    B floor=2
    B floor=3
    B floor=4
    config.cls_level1()=B floor=11
    B floor=22
    isinstance(l1, A)=True
    isinstance(l2, A)=True
    isinstance(l2, config.level2.cls_level2)=True
    isinstance(l2, config.cls_level1)=False