Search code examples
pythonpython-typingpydanticpython-dataclasses

How to typehint dynamic class instantiation like pydantic and dataclass?


Both Pydantic and Dataclass can typehint the object creation based on the attributes and their typings, like these examples:

from pydantic import BaseModel, PrivateAttr, Field
from dataclasses import dataclass

# Pydantic way
class Person(BaseModel):

    name : str
    address : str
    _valid : bool = PrivateAttr(default=False)


#dataclass way
@dataclass
class PersonDataclass():

    name : str
    address : str
    _valid : bool = False

bob = Person(name="Bob", address="New York")

bobDataclass = PersonDataclass("Bob", "New York")

With this code, I can get typehint on object creation (see screenshots below):

pydantic typehint on object creation

dataclass typehint on object creation

Not only that, but the object's attributes also get documented.


I studied the code of pydantic to try to achieve the same result, but I couldn't. The code that I tried was this:

class MyBaseModelMeta(type):

    def __new__(cls, name, bases, dct):

        def new_init(self : cls, /, name : str, address : str):
            self.name = name
            self.address = address
            self._valid = False

        dct["__init__"] = new_init
        dct["__annotations__"] = {"__init__": {"name": str, "address": str, "_valid": bool}}
        return super().__new__(cls, name, bases, dct)


class MyBaseModel(metaclass=MyBaseModelMeta):
    
    def __repr__(self) -> str:
        return f"MyBaseModel: {self.__dict__}"


class MyPerson(MyBaseModel):
    pass

myBob = MyPerson("Bob", "New York")

My class works (the dynamic init insertion works) but the class and object get no typehint.

my class works but it doesn't get typehinted

What am I doing wrong? How can I achieve the typehints?


Solution

  • @Daniil Fajnberg is mostly correct,

    but depending on your type checker you can can use the dataclass_transform(Python 3.11)

    or __dataclass_transform__ early adopters program decorator.

    Pylance and Pyright (usually used in VS-Code) at least work with these.

    You can only mimic the behaviour of dataclasses that way though, I don't think you're able to define that your Metaclass adds extra fields. :/

    Edit: At least pydantic uses this decorator for their BaseModel: https://pydantic-docs.helpmanual.io/visual_studio_code/#technical-details

    If you dig through the code of pydantic you'll find that their ModelMetaclass is decorated with __dataclass_transform__