3

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?

  • Type hints are for *static* analysis; they don't serve any purpose in dynamically defined classes. Your IDE isn't going to try executing your code to find out what annotations you add at *runtime*. – chepner Oct 07 '22 at 12:49
  • @chepner so how does pydantic and dataclass do it? The hint that is provided to IDE is dynamic – Murilo Rocha Oct 07 '22 at 12:51
  • 1
    The IDE could be special-cased to examine code using Pydantic. – chepner Oct 07 '22 at 16:02

2 Answers2

4

@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
  • 12,753
  • 2
  • 10
  • 41
3

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

Robin Gugel
  • 853
  • 3
  • 8
  • You are right, but to be fair, `dataclass_transform` is just a standardization of the "hack" that those modules use. It is still something to look forward to. But I would nonetheless argue that "dynamic type annotations" is a contradiction in terms. – Daniil Fajnberg Oct 07 '22 at 15:32
  • Yeah that's correct. I've used `__dataclass_transform__` to at least get the same type hints that pydantic models get in VS-Code. But especially the example asked by OP isn't possible as type hints cannot be added dynamically. – Robin Gugel Oct 07 '22 at 17:47