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 identicaltypeobjects: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.
