Search code examples
pythonclass-attributes

How to define a class with dynamic attributes?


In my project, I need to create a class with attributes passed by a dict, something like this:

class_attributes = {"sensor": Nested(Sensor),
                    "serial_interface": Nested(SerialInterface)}
class MSchema(marshmallow.ModelSchema):
    class Meta:
        model = cls

    attr = class_attributes

I need that "sensor" and "serial_interface" to be in the class, and can be access using MSchema.sensor or MSchema.serial_interface.


Solution

  • You can call the metaclass of ModelSchema directly, rather than defining the class declaratively using a class statement.

    m = marshmallow.ModelSchema
    
    class_attributes = {
        "sensor": Nested(Sensor),
        "serial_interface": Nested(SerialInterface)
    }
    
    m = marshmallow.ModelSchema
    mc = type(m)
    MSchema = mc('MSchema', (m,), {
        'Meta': type('Meta', (), {'model': cls}),
        **class_attributes
        })
    

    In case you aren't aware, a class statement is just a declarative syntax for calling type (or some other metaclass) with 3 arguments: the name of the class, a tuple of parent classes, and a dict of class attributes. The class statement evaluates its body to produce the dict, then calls type (or another given metaclass), and binds the return value to the name. Some simpler examples:

     # Foo = type('Foo', (), {})
     class Foo:
         pass
    
     # Foo = Bar('Foo', (), {})
     class Foo(metaclass=Bar):
         pass
    
     # Foo = Bar('Foo', (Parent,), {'x': 3})
     class Foo(Parent, metaclass=Bar):
         x = 3
    
     # def foo_init(self, x):
     #     self.x = x 
     # Foo = Bar('Foo', (), {'__init__': foo_init})
     class Foo(metaclass=Bar):
         def __init__(self, x):
             self.x = x