I have a class factory which dynamically builds new types (not objects/instances). The code is currently working per-se, but I can’t figure out where the attributes I give it are stored. Below is an image of my driver code and output:
All attributes and methods are set and working as expected, but they’re not registered in the __dict__
property. This is not ok, since a downstream use case will search __dict__
for those custom types. The entire “working” factory is here:
from typing import Iterable, Any from types_extensions import void class DynamicClassFactory: def __init__(self, default_base_classes: Iterable[type] = None, prefix: str = '', suffix: str = '', attributes: dict[str, Any] = None) -> void: self.prefix: str = prefix self.suffix: str = suffix self.mixins: list[type] = [x for x in default_base_classes or (object,)] self.attributes: dict[str, Any] = attributes or {} def build(self, class_name: str, extra_mixins: Iterable[type] = (), attributes: dict[str, Any] = None) -> type: return type( self.prefix + class_name + self.suffix, self._create_bases(extra_base_classes=extra_mixins), {**self.attributes, **(attributes or {})} ) def _create_bases(self, extra_base_classes: Iterable[type]) -> tuple[type]: return tuple(self.mixins + [x for x in extra_base_classes])
I have looked online and mainly used this as a guide: https://realpython.com/python-metaclasses/ (for this particular use case of type(), Ctrl+F in the page and search “You can also call type() with three arguments” without the quotes.
Thanks in advance!
PS: I know I don’t have correct error handling for illegal characters in names, it’s planned.
Advertisement
Answer
When you pass attributes to the type
class, you’re actually passing class attributes instead of instance attributes. From documentation:
The dict dictionary contains attribute and method definitions for the class body; it may be copied or wrapped before becoming the
__dict__
attribute. The following two statements create identicaltype
objects:class X: a = 1 X = type('X', (), dict(a=1))
So the following holds true:
my_class2 = factory.build(...) print(my_class2.__dict__) # Outputs {..., x: 1, y: 2} obj2 = my_class2() print(obj2.__dict__) # Outputs {}
You may ask how can you access obj2.y
successfully. That’s because when Python does not find a attribute in the instance (in this case, obj2
), it’ll look up in the class attributes (in this case, my_class2
). Since there is one, it’ll evaluate to it.