Reputation: 155
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?
Upvotes: 5
Views: 2720
Reputation: 18568
@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.
Upvotes: 7
Reputation: 1216
@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__
Upvotes: 5