# How would I create a Parameterized class programmatically?

**URL:** <https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149>\
**Category:** Param\
**Created:** [December 7, 2021, 9:01pm UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149 "2021-12-07T21:01:34Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Marc](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/marc/32/24_2.png) [@Marc](https://discourse.holoviz.org/u/Marc)\
**Post date:** [December 7, 2021, 9:01pm UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/1 "2021-12-07T21:01:34Z")

</div>

I’m exploring converting a Pydantic class to a Param class programmatically.

Say I have

```python
class_name= "MyClass"
parameters = [
    {"name": "value", "type": param.String, "default": "bla", "allow_None": True},
    {"name": "value2", "type": param.Integer, "default": "blabla", "allow_None": False}
]

```

How would I programmatically create the class below from this

```python
class MyClass(param.Parameterized):
    value = param.String(default="bla", allow_None= True)
    value2 = param.Integer(default="blabla", allow_None= False)

```

Thanks.

---

<div class="post-metadata">

**Author:** ![Marc](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/marc/32/24_2.png) [@Marc](https://discourse.holoviz.org/u/Marc)\
**Post date:** [December 8, 2021, 6:52am UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/2 "2021-12-08T06:52:04Z")

</div>

Figured it out. I can use `.param.add_parameter`.

Which means I’m able to do basic conversions between Param models and Pydantic Models via the functions `param_to_pydantic_class` and `pydantic_to_param_class`.

```python
import datetime
from typing import Container, Dict, List, Optional, Type, Union

import param
import pydantic
import pytest
from pydantic import BaseConfig, BaseModel, create_model

try:
    import numpy as np

    DATE_TYPE = Union[datetime.datetime, datetime.date, np.datetime64]
except:
    DATE_TYPE = Union[datetime.datetime, datetime.date]

PARAM_TO_PYTHON_TYPE: Dict[param.Parameter, Type] = {
    param.String: str,
    param.Integer: int,
    param.Number: float,
    param.Date: DATE_TYPE,
    param.List: List,
}
PYTHON_TYPE_TO_PARAM = {value: key for key, value in PARAM_TO_PYTHON_TYPE.items()}

def _get_python_type_from_parameter(parameter: param.Parameter):
    if isinstance(parameter, param.List) and parameter.item_type:
        python_type: Type = List[parameter.item_type]
    else:
        python_type = PARAM_TO_PYTHON_TYPE[parameter. __class__]

    if parameter.allow_None:
        python_type = Union[python_type, None]

    return python_type

def _get_parameter_type_from_python_type(type_: Type) -> Type[param.Parameter]:
    return PYTHON_TYPE_TO_PARAM[type_]

def _get_parameter_type_from_pydantic_field(
    field: pydantic.fields.ModelField,
) -> Type[param.Parameter]:
    return _get_parameter_type_from_python_type(field.type_)

def param_to_pydantic_class(
    parameterized: Type[param.Parameterized],
    *,
    config: Type = BaseConfig,
    exclude: Container[str] = []
) -> Type[BaseModel]:
    fields = {}
    parameters = [
        parameterized.param[key]
        for key in parameterized.param
        if not parameterized.param[key].name in exclude
    ]
    for parameter in parameters:
        python_type = _get_python_type_from_parameter(parameter)
        fields[parameter.name] = (python_type, parameter.default)
    pydantic_model = create_model(parameterized. __name__ , __config__ =config, **fields) # type: ignore
    return pydantic_model

def pydantic_to_param_class(value) -> param.Parameterized:
    new_class: param.Parameterized = type(value. __name__ , (param.Parameterized,), {})

    for name, field in value. __fields__.items():
        parameter_type = _get_parameter_type_from_pydantic_field(field)
        parameter = parameter_type(default=field.default, allow_None=field.allow_none)
        new_class.param.add_parameter(name, parameter)
    return new_class

```

---

<div class="post-metadata">

**Author:** ![Material-Scientist](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/material-scientist/32/503_2.png) [@Material-Scientist](https://discourse.holoviz.org/u/Material-Scientist)\
**Post date:** [April 4, 2023, 8:03pm UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/3 "2023-04-04T20:03:04Z")

</div>

Not sure if it’s of any use, but here’s another way if the other object has a `to_dict()` method:

```auto
from telethon.tl.patched import Message

# Metaclass, so we can inherit from both
class Meta(type(param.Parameterized),type(Message)):
    pass

# Derived class that inherits both
class MessageP(param.Parameterized,Message,metaclass=Meta):
    def __init__ (self,**kwargs):
        # Only pass supported kwargs
        keys = inspect.getfullargspec(Message).args
        super(). __init__ (
            _client=kwargs['_client'],
            _input_chat=kwargs['_input_chat'],
            **{key:kwargs[key] for key in kwargs if key in keys}
        )

# Create parameterized MSG class from msg-dict
MSG = param.parameterized_class(
    'Message', # Class name
    params=param.guess_param_types(
        _client=telethon.client.telegramclient.TelegramClient, # pass additional kwargs
        _input_chat=telethon.tl.types.InputPeerChat, # pass additional kwargs
        **msg.to_dict() # exported kwargs
    ), # dict(name,Parameter)
    bases=MessageP # Derived class as base
)

test = MSG(
    _client=msgs[-3].client,
    _input_chat=msgs[-3].input_chat,
    **msgs[-3].to_dict() # We can now instantiate this class with different param values
)

test

```

Output:

 ![image](https://discourse.holoviz.org/uploads/default/original/2X/7/7d7971716671d736eaccbb275b158c88d06694f9.png)  
 ![image](https://discourse.holoviz.org/uploads/default/original/2X/7/700ba95549f14fbd2a629e9d962c51c5359f0b06.png)  
 ![image](https://discourse.holoviz.org/uploads/default/original/2X/2/2608e0d7d15720af5b9eba54a653ff1c052e5dc1.png)

I’ve never dealt with meta-classes before, but it was required for inheriting from both classes.

The code above creates a parameterized class from a derived class that inherits from both the Telegram Message class as well as param.Parameterized. Given how `to_dict()` produces some kwargs that are not allowed in Message instantiation, I’ve set the derived class to only pass kwargs that are in both.

---

<div class="post-metadata">

**Author:** ![ahuang11](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/ahuang11/32/4947_2.png) [@ahuang11](https://discourse.holoviz.org/u/ahuang11)\
**Post date:** [August 30, 2023, 12:28am UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/4 "2023-08-30T00:28:16Z")

</div>

Here’s another version of param to pydantic:

```python
[import datetime
from typing import Dict, List, Literal, Optional, Tuple, Type, TypeVar, Union

import inspect
import param
from pydantic import BaseConfig, BaseModel, create_model
from pydantic.color import Color
from pydantic.fields import FieldInfo
from typing_extensions import Self
from pydantic.fields import Undefined

DATE_TYPE = Union[datetime.datetime, datetime.date]
PARAM_TYPE_MAPPING: Dict[param.Parameter, Type] = {
    param.String: str,
    param.Integer: int,
    param.Number: float,
    param.Boolean: bool,
    param.Event: bool,
    param.Date: DATE_TYPE,
    param.DateRange: Tuple[DATE_TYPE],
    param.CalendarDate: DATE_TYPE,
    param.CalendarDateRange: Tuple[DATE_TYPE],
    param.ListSelector: List,
    param.Parameter: object,
    param.Color: Color,
}
PandasDataFrame = TypeVar("pandas.core.frame.DataFrame")

class ArbitraryTypesModel(BaseModel):
    """
    A Pydantic model that allows arbitrary types.
    """

    class Config(BaseConfig):
        arbitrary_types_allowed = True

def _create_literal(obj: List[Union[str, Type]]) -> Type:
    """
    Create a literal type from a list of objects.
    """
    types = []
    for obj in obj:
        if obj is None:
            continue
        elif isinstance(obj, str):
            types.append(obj)
        else:
            types.append(obj. __name__ )
    if types:
        return Literal[tuple(types)]
    else:
        return str

def parameter_to_field(
    parameter: param.Parameter, created_models: Dict[str, BaseModel]
) -> (Type, FieldInfo):
    """
    Translate a parameter to a pydantic field.
    """
    param_type = parameter. __class__
    description = " ".join(parameter.doc.split()) if parameter.doc else None
    field_info = FieldInfo(description=description)

    if param_type in PARAM_TYPE_MAPPING:
        type_ = PARAM_TYPE_MAPPING[param_type]
        field_info.default = parameter.default
    elif param_type is param.ClassSelector:
        type_ = parameter.class_
        try:
            if issubclass(type_, param.Parameterized):
                type_ = created_models.get(type_. __name__ , type_. __name__ )
        except TypeError:
            pass
        if isinstance(type_, tuple):
            type_ = _create_literal(type_)
        if parameter.default is not None:
            default_factory = parameter.default
            if not callable(default_factory):
                default_factory = type(default_factory)
            field_info.default_factory = default_factory
    elif param_type is param.List:
        type_ = List
        if parameter.default == []:
            field_info.default_factory = list
        elif parameter.default is not None:
            field_info.default_factory = parameter.default
    elif param_type is param.Dict:
        type_ = Dict
        if parameter.default == {}:
            field_info.default_factory = dict
        elif parameter.default is not None:
            field_info.default_factory = parameter.default
    elif param_type in [param.Selector, param.ObjectSelector]:
        if parameter.objects:
            type_ = _create_literal(parameter.objects)
        else:
            type_ = str
    elif issubclass(param_type, param.DataFrame):
        type_ = PandasDataFrame
    elif parameter.name == "align":
        type_ = _create_literal(["auto", "start", "center", "end"])
    elif parameter.name == "aspect_ratio":
        type_ = Union[Literal["auto"], float]
    elif parameter.name == "margin":
        type_ = Union[float, Tuple[float, float], Tuple[float, float, float, float]]
    else:
        raise NotImplementedError(
            f"Parameter {parameter.name!r} of {param_type. __name__!r} not supported"
        )
    return type_, field_info

def param_to_pydantic(
    parameterized: Type[param.Parameterized],
    base_model: Type[BaseModel] = ArbitraryTypesModel,
    created_models: Optional[Dict[str, BaseModel]] = None,
) -> Dict[str, BaseModel]:
    """
    Translate a param Parameterized to a Pydantic BaseModel.

    Parameters
    ----------
    parameterized : Type[param.Parameterized]
        The parameterized class to translate.
    base_model : Type[BaseModel], optional
        The base model to use, by default ArbitraryTypesModel
    created_models : Optional[Dict[str, BaseModel]], optional
        A dictionary of already created models, by default None
    """
    parameterized_name = parameterized. __name__
    if created_models is None:
        created_models = {}
    if parameterized_name in created_models:
        return created_models

    parameterized_signature = inspect.signature(parameterized. __init__ )
    required_args = [
        arg.name
        for arg in parameterized_signature.parameters.values()
        if arg.name not in ["self", "params"] and arg.default == inspect._empty
    ]

    fields = {}
    for parameter_name in parameterized.param:
        parameter = parameterized.param[parameter_name]
        if hasattr(parameter, "class_") and hasattr(parameter.class_, "param"):
            parameter_class_name = parameter.class_. __name__
            if parameterized_name != parameter_class_name:
                param_to_pydantic(parameter.class_, created_models=created_models)
        type_, field_info = parameter_to_field(parameter, created_models)
        if parameter_name == "schema":
            field_info.alias = "schema"
            parameter_name = "schema_"
        elif parameter_name == "copy":
            field_info.alias = "copy"
            parameter_name = "copy_"
        elif parameter_name[0] == "_":
            field_info.alias = parameter_name
            parameter_name = parameter_name.lstrip("_")

        if parameter_name in required_args:
            field_info.default = Undefined
        elif field_info.default == Undefined and not field_info.default_factory:
            field_info.default = None](https://github.com/holoviz-dev/lumen-llm)

        fields[parameter_name] = (type_, field_info)

    pydantic_model = create_model(parameterized. __name__ , __base__ =base_model, **fields)
    created_models[pydantic_model. __name__] = pydantic_model
    return created_models

```

---

<div class="post-metadata">

**Author:** ![jbednar](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/jbednar/32/23_2.png) [@jbednar](https://discourse.holoviz.org/u/jbednar)\
**Post date:** [August 31, 2023, 10:03pm UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/5 "2023-08-31T22:03:02Z")

</div>

@ahuang11 , can you make a PR to put that into Param or at least its docs?

---

<div class="post-metadata">

**Author:** ![ahuang11](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/ahuang11/32/4947_2.png) [@ahuang11](https://discourse.holoviz.org/u/ahuang11)\
**Post date:** [August 31, 2023, 11:22pm UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/6 "2023-08-31T23:22:41Z")

</div>

Mentioned it here for near future

> <https://github.com/holoviz/param/issues/828>
>
> Jim mentioned adding what Marc and I shared on Discourse to param, perhaps as a …util(?), or simply briefly mention in docs.
> https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/4?u=ahuang11
> 
> Note, this does not handle converting param methods at all; simply incompletely translating the parameters into fields.
> 
> The opposite issue (converting from pydantic -\> param) can be found here: https://github.com/holoviz/param/issues/809

---

<div class="post-metadata">

**Author:** ![dwr-psandhu](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/dwr-psandhu/32/531_2.png) [@dwr-psandhu](https://discourse.holoviz.org/u/dwr-psandhu)\
**Post date:** [November 29, 2023, 5:17pm UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/7 "2023-11-29T17:17:51Z")

</div>

I am looking to generate a parameterized class based on a python object that would wrap specified fields with their current inspected type and serve as a pass through for specified methods

This would allow arbitrary objects to be wrapped just in time to represent them in a UI and call actions on them

Have you done or seen anything similar?

---

<div class="post-metadata">

**Author:** ![dwr-psandhu](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/dwr-psandhu/32/531_2.png) [@dwr-psandhu](https://discourse.holoviz.org/u/dwr-psandhu)\
**Post date:** [November 30, 2023, 4:55am UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/8 "2023-11-30T04:55:09Z")

</div>

NVM, it was quite easy following the thread above

```auto
import param
from typing import Any, List, Type

def object_to_param_class(obj: Any, attributes: List[str], methods: List[str]) -> Type[param.Parameterized]:
    # Store the original object in the param class
    class ParamWrapper(param.Parameterized):
        _original_object = obj

        def __setattr__ (self, key, value):
            # Set attribute both on param class and original object
            super(). __setattr__ (key, value)
            if key in attributes:
                setattr(self._original_object, key, value)

    param_attrs = {'_original_object': obj}

    # Handling attributes
    for attr_name in attributes:
        attr_value = getattr(obj, attr_name)
        attr_type = type(attr_value)

        # Choose appropriate param type based on attribute type
        if attr_type is int:
            param_attrs[attr_name] = param.Integer(default=attr_value)
        elif attr_type is float:
            param_attrs[attr_name] = param.Number(default=attr_value)
        elif attr_type is str:
            param_attrs[attr_name] = param.String(default=attr_value)
        elif attr_type is bool:
            param_attrs[attr_name] = param.Boolean(default=attr_value)
        # Add more type mappings as necessary

    # Handling methods
    for method_name in methods:
        if hasattr(obj, method_name) and callable(getattr(obj, method_name)):
            method = getattr(obj, method_name)
            param_attrs[method_name] = method

    # Dynamically create the param class
    param_class = type(obj. __class__. __name__ + 'Param', (ParamWrapper,), param_attrs)
    return param_class

def get_methods(obj):
    return [attr for attr in dir(obj) 
           if callable(getattr(obj, attr)) and not attr.startswith('__')]

def get_attributes(obj):
    return [attr for attr in dir(obj) 
           if not callable(getattr(obj, attr)) and not attr.startswith('__')]

```

---

<div class="post-metadata">

**Author:** ![ahuang11](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/ahuang11/32/4947_2.png) [@ahuang11](https://discourse.holoviz.org/u/ahuang11)\
**Post date:** [May 14, 2025, 12:22am UTC](https://discourse.holoviz.org/t/how-would-i-create-a-parameterized-class-programmatically/3149/9 "2025-05-14T00:22:23Z")

</div>

Lumen now has a decently robust translator here:

> <https://github.com/holoviz/lumen/blob/d3b9dfe732898adde02fd643dde7f92d0bca0ed6/lumen/ai/translate.py>

Example:

```python
from pydantic import BaseModel, Field
from typing import Union

class Pet(BaseModel):
    name: str = Field(..., description="Name of the pet")
    age: int = Field(..., ge=0, le=20, description="Age of the pet in years")

class Dog(Pet):
    bark_volume: int = Field(default=5, ge=0, le=10)

class Cat(Pet):
    meow_pitch: float = Field(default=0.5, gt=0, lt=1)

class Owner(BaseModel):
    pet: Union[Dog, Cat]

Owner.schema()

```

```python
import param
from lumen.ai.translate import param_to_pydantic

class Pet(param.Parameterized):
    name = param.String(doc="Name of the pet")
    age = param.Integer(default=1, bounds=(0, 20), doc="Age of the pet in years")

class Dog(Pet):
    bark_volume = param.Integer(default=5, bounds=(0, 10))

class Cat(Pet):
    meow_pitch = param.Number(default=0.5, bounds=(0, 1))

class Owner(param.Parameterized):
    pet = param.ClassSelector(class_=(Dog, Cat))

response_model = param_to_pydantic(Owner, excluded=["name"])["Owner"]
response_model.schema()

```
