admin管理员组

文章数量:1279124

I have a Pydantic class to represent how my general API response should look:

class RootDataResponseSchema(BaseModel):
    status: Literal["success", "fail"]
    data: BaseModel

I went with BaseModel for data, as the idea would be I'd subclass RootDataResponseSchema and add more specificity to specific endpoint responses, such as:

class NewItemResponse(RootDataResponseSchema):
    status: Literal["success"] = "success"
    data: NewItemResponseData

The problem I've run into seems to only affect the IDE (in my case VSCode), so I assume is a notice from pylance, but tells me that both status and data have changed from the root class. It does mention it's irrelevant, but as a subclassed object does count as the superclass in terms of comparison, is there a mechanism for narrowing the scope of type hints in Python? Or is this just an artifact of loosely typed Python?

I have a Pydantic class to represent how my general API response should look:

class RootDataResponseSchema(BaseModel):
    status: Literal["success", "fail"]
    data: BaseModel

I went with BaseModel for data, as the idea would be I'd subclass RootDataResponseSchema and add more specificity to specific endpoint responses, such as:

class NewItemResponse(RootDataResponseSchema):
    status: Literal["success"] = "success"
    data: NewItemResponseData

The problem I've run into seems to only affect the IDE (in my case VSCode), so I assume is a notice from pylance, but tells me that both status and data have changed from the root class. It does mention it's irrelevant, but as a subclassed object does count as the superclass in terms of comparison, is there a mechanism for narrowing the scope of type hints in Python? Or is this just an artifact of loosely typed Python?

Share Improve this question edited Feb 24 at 23:27 Barmar 782k56 gold badges546 silver badges660 bronze badges asked Feb 24 at 23:21 RohitRohit 3,1563 gold badges34 silver badges64 bronze badges 3
  • Literal['success', 'fail'] and Literal['success'] are two very distinct sets of items, the latter is not an instance or subclass of the former. You shouldn't get any warnings for status if you just remove the type hint, because 'success' is a valid item from the literal type declared in the base class. – NotAName Commented Feb 25 at 0:57
  • I tried removing the status hint, but then Pydantic gives an error: Field 'status' defined on a base class was overridden by a non-annotated attribute. All field definitions, including overrides, require a type annotation. – Rohit Commented Feb 25 at 2:23
  • PEP 767 is under consideration to solve the problem you're having. Of course, if you use this it means you can't re-assign the value of status after initialisation, but I presume it's rare that you'd want to. – dROOOze Commented 5 hours ago
Add a comment  | 

2 Answers 2

Reset to default 2

Those annotations don't just say what types can be observed for those attributes. They also say what types can be assigned to those attributes. Your annotations claim that if you have an instance of RootDataResponseSchema, it is okay to perform

obj.status = 'fail'

Your subclass's annotations say that this assignment is not okay, hence the conflict.


If you don't want to allow these assignments, you can indicate that to type checkers with typing.Final:

from typing import Final, Literal

class RootDataResponseSchema(BaseModel):
    status: Final[Literal["success", "fail"]]
    data: Final[BaseModel]

class NewItemResponse(RootDataResponseSchema):
    status: Final[Literal["success"]] = "success"
    data: Final[NewItemResponseData]

I am not sure whether Pydantic will do the right thing with these annotations, however. The Pydantic source code looks like it treats typing.Final as marking a field frozen, but I can't find any documentation about the behavior, only a bug report about inconsistent handling from 2023, closed as completed with no pull request linked.

If you want a type with exactly two values, define an enumerated type. (While we're redefining things, your base class should probably be generic in the type of data.)

T = TypeVar('T', bound=BaseModel)

class StatusType(StrEnum):
    SUCCESS = "success"
    FAILURE = "fail"


class RootDataResponseSchema(BaseModel, Generic[T]):
    status: StatusType
    data: T

class NewItemResponse(RootDataResponseSchema[NewItemResponseData]):
    status = StatusType.SUCCESS

本文标签: pythonNarrowing typing on a subclassStack Overflow