vsnag
vsnag

Reputation: 53

using isintance on a pydantic model

I am expecting multiple data types as input to a function & want to take a specific action if its a pydantic model (pydantic model here means class StartReturnModel(BaseModel)). In case of model instance I can check it, using isinstance(model, StartReturnModel) or isinstance(model, BaseModel) to identify its a pydantic model instance. Based on the below test program I can see that type(StartReturnModel) returns as ModelMetaclass. Can I use this to identify a pydantic model? or is there any better way to do it?

from pydantic.main import ModelMetaclass
from typing import Optional

class StartReturnModel(BaseModel):
    result: bool
    pid: Optional[int]

print(type(StartReturnModel))
print(f"is base model: {bool(isinstance(StartReturnModel, BaseModel))}")
print(f"is meta model: {bool(isinstance(StartReturnModel, ModelMetaclass))}")

res = StartReturnModel(result=True, pid=500045)
print(f"\n{type(res)}")
print(f"is start model(res): {bool(isinstance(res, StartReturnModel))}")
print(f"is base model(res): {bool(isinstance(res, BaseModel))}")
print(f"is meta model(res): {bool(isinstance(res, ModelMetaclass))}")

*****Output****
<class 'pydantic.main.ModelMetaclass'>
is base model: False
is meta model: True

<class '__main__.StartReturnModel'>
is start model(res): True
is base model(res): True
is meta model(res): False

Upvotes: 2

Views: 6770

Answers (2)

Daniil Fajnberg
Daniil Fajnberg

Reputation: 18683

After you expanded a bit in the comment thread, it is clear that you have a fundamental gap in understanding of Python classes and metaclasses. The topics have been discussed at length on SO, so I'll just refer you to the search function for details, but the short answer to your particular question is this:

from pydantic import BaseModel
from pydantic.main import ModelMetaclass


class MyModel(BaseModel):
    x: int
    y: str


obj = MyModel(x=1, y="a")
cls = MyModel

print(f"{isinstance(obj, BaseModel)=}")
print(f"{isinstance(obj, MyModel)=}")
print(f"{issubclass(cls, BaseModel)=}")
print(f"{issubclass(cls, MyModel)=}")
print(f"{cls is MyModel=}")
print(f"{isinstance(cls, ModelMetaclass)=}")  # just to illustrate
print(f"{isinstance(cls, type)=}")            # just to illustrate

Output:

isinstance(obj, BaseModel)=True
isinstance(obj, MyModel)=True
issubclass(cls, BaseModel)=True
issubclass(cls, MyModel)=True
cls is MyModel=True
isinstance(cls, ModelMetaclass)=True
isinstance(cls, type)=True

You should avoid using pydantic.main.ModelMetaclass because it is currently not (fully) exposed publicly as you correctly noted. And as you can see from the code above, there is simply no need to deal with it for you.

If you have a function that is supposed to handle both instances of a model class and specific classes that inherit from BaseModel, that could look like this:

from typing import Union
from pydantic import BaseModel


def do_stuff(obj_or_cls: Union[BaseModel, type[BaseModel]]) -> None:
    if isinstance(obj_or_cls, BaseModel):
        print(f"Got an instance of the model `{obj_or_cls.__class__.__name__}`")
    elif isinstance(obj_or_cls, type) and issubclass(obj_or_cls, BaseModel):
        print(f"Got a model subclass called `{obj_or_cls.__name__}`")
    else:
        raise TypeError


class MyModel(BaseModel):
    x: int
    y: str


obj = MyModel(x=1, y="a")
cls = MyModel


do_stuff(obj)
do_stuff(cls)

Output:

Got an instance of the model `MyModel`
Got a model subclass called `MyModel`

Upvotes: 3

Samuel Colvin
Samuel Colvin

Reputation: 13349

Yes you can use it, but why not use isinstance or issubclass.

Upvotes: 2

Related Questions