简体   繁体   中英

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?

@chepner is right.

Static type checkers don't execute your code, they just read it.

And to answer your question how Pydantic and dataclasses do it - they cheat:

Special plugins allow mypy to infer the signatures that are actually only created at runtime. (I am just joking about the "cheating" of course, but you get my point.)

If you want your own dynamic annotations to be considered by static type checkers, you will have to write your own plugins for them.

@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__

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM