red888
red888

Reputation: 31652

How does class polymorphism work with pydantic models?

I have two classes that inherit from the same parent:

class ChildOne(BaseSettings):
    model_config = SettingsConfigDict(
        extra="allow",
    )
    ...

class ChildTwo(BaseSettings):
    model_config = SettingsConfigDict(
        extra="forbid",
    )
    ...

I should be able to pass ChildOne and ChildTwo to anything that accepts BaseSettings right?

I cannot figure out how to get this to work and satisfy strict type checking.

I tried this, but the cast wipes out model_config set for the child and I'm still getting the two issues commented above the return statement

def my_func(model: BaseSettings) -> BaseSettings:
    # Object of type "BaseSettings" is not callable
    # Return type is unknown
    return model()

# This cast seems to completely wipe out model_config
my_func(cast(ChildOne, BaseSettings))
my_func(cast(ChildTwo, BaseSettings))

Edit

After some googling this works, but I'm still not 100% why or if there is a cleaner way:

C = TypeVar("C", bound=BaseSettings)
def my_func(model: Type[C]) -> BaseSettings:

Do I have to explicitly tell the type checker that I want to accept any sub class of BaseSettings because it doesn't allow that automatically? Isn't that how basic class polymorphism is suppose to work?

This ^ also doesn't really work because when I try to access fields of C inside my_func I get:

Cannot access member "<Some field of ChildTwo|ChildOne>" for type "BaseSettings"
  Member "<Some field of ChildTwo|ChildOne>" is unknown

Edit

I wasn't using generics correctly, I need to do:

C = TypeVar("C", bound=BaseSettings)
# Returns same type passed in, not BaseSettings type
def my_func(model: Type[C]) -> C:

I'm still confused as to why I need to use generics in the first place. Would I need to do something like this in C#/Java as well?

Upvotes: 0

Views: 1207

Answers (1)

Yurii Motov
Yurii Motov

Reputation: 2353

Here you specify the type of model as 'instance object of either BaseSettings class or any class, inherited from BaseSettings':

def my_func(model: BaseSettings) -> BaseSettings:

But it seems like you want to pass class itself (not an instance object of class). So, your second attempt is the right way to specify the type of model:

C = TypeVar("C", bound=BaseSettings)
def my_func(model: Type[C]) -> BaseSettings:

But dont forget that in this case model is not an instance object of class, but class itself. And it doesn't have attributes that you defined in your ChildOne and ChildTwo. You need to create an instance object of this class to access attributes.

C = TypeVar("C", bound=BaseSettings)
def my_func(model: Type[C]) -> BaseSettings:
    instance = model()
    # instance.some_attribute
    return instance

Or, do you want something like this?

def my_func(model: BaseSettings) -> BaseSettings:
    # model.some_attribute = something
    return model

res = my_func(ChildOne()) # passing instance

Upvotes: 0

Related Questions