Python 3.12 中对于使用父类方法而无需键入 module 的继承类的类型提示

问题描述 投票:0回答:1

我有一个父类,以及许多从父类继承的子类。这些类本质上只是 python 模型,用于从 json 或 MongoDB 读取某些文档,并将它们从字典更改为类。因此,我有一个标准的

from_file
类方法,仅在父类中实现。我希望得到类型提示,以便使用这些类的其他模块知道将根据调用该方法的类返回哪个类。

家长班:

class ParentDoc(ABC):
    def __init__(
        self,
        ver: int = 1,
    ) -> None:
        self.ver = ver

    def to_dict(self) -> dict:
        """Converts the object to a dictionary.

        Returns:
            dict: A dictionary representation of the object.
        """
        data = self.__dict__.copy()
        data["_class"] = self.__class__.__name__
        return data

    @classmethod
    def from_dict(cls, data: dict) -> ParentDoc:
        """Creates an instance from a dictionary.

        Args:
            data (dict): A dictionary representation of the object.

        Returns:
            DbDoc: An instance of the class.
        """
        return cls(
            ver=data.get("ver", 1),
        )

    def to_file(self, file_path: str | Path) -> None:
        """Writes the object to a file.

        Args:
            file_path (str | Path): The file path.
        """
        with open(file_path, "w", encoding="utf8") as json_file:
            json.dump(self.to_dict(), json_file)

    @classmethod
    def from_file(cls, file_path: str | Path) -> ?????:
        """Creates an instance from a file.

        Args:
            file_path (str | Path): The file path.

        Returns:
            DbDoc: An instance of the class.
        """
        with open(file_path, "w", encoding="utf8") as json_file:
            data = create_from_dict(json.load(json_file))
        return data

下面包含

from_file
方法中的辅助函数。

def create_from_dict(data: dict):
    """Creates an object from a dictionary based on the _class field.

    Args:
        data (dict): A dictionary representation of the object.

    Returns:
        An instance of the appropriate class.
    """
    class_name = data.get("_class")
    if class_name == "ParentDoc":
        return ParentDoc.from_dict(data)
    elif class_name == "ChildDoc":
        return ChildDoc.from_dict(data)
    elif class_name == "Child2Doc":
        return Child2Doc.from_dict(data)
    else:
        raise ValueError(f"Unsupported class: {class_name}")

然后所有子类都重载 to_dict 和 from_dict 方法,但不重载 to_file 和 from_file 方法。

class ChildDoc(DbDoc):
    """Base class for a generic child.

    Attributes:
        name (str): The name of the document.
        ver (int): The version of the document.
    """

    def __init__(
        self,
        name: str,
        ver: int = 1,
    ) -> None:
        """
        Args:
            name (str): The name of this document.
            ver (int, optional): The version of the document. Defaults to 1.
        """
        super().__init__(ver=ver)
        self.name = name

    def to_dict(self) -> dict:
        """Converts the object to a dictionary"""
        data = super().to_dict()
        data.update(
            {
                "name": self.name,
            }
        )
        return data

    @classmethod
    def from_dict(cls, data: dict) -> ChildDoc:
        """Creates an instance from a dictionary.

        Args:
            data (dict): A dictionary representation of the object.

        Returns:
            ChildDoc: An instance of the class.
        """
        return cls(
            name=data["name"],
        )

我应该在父类中所有问号所在的位置放置什么,以便我可以调用

ChildDoc.from_file(json_path)
并且类型提示将理解它将返回 ChildDoc 对象而不是 ParentDoc 对象?我目前没有任何输出类型提示,因此 linter 认为它可能是任何父类或子类,即使我使用一个特定的子类来调用它。我想我也可以在
create_from_dict
函数上有更好的类型提示。

我想在 Python 3.12 中使用标准类型提示(不必导入

typing
模块)。我已经尝试过
Self
但没有成功。

python inheritance python-typing
1个回答
0
投票

要为

from_file
方法实现正确的类型提示,您必须确保类型提示反映正确的子类。在 Python 3.12 中,您可以使用
cls
作为类本身的占位符。类方法中的
cls
指的是被调用的类,因此如果您使用
ChildDoc
.
from_file
,类型提示将正确反映
ChildDoc
.

我在必要的地方修改了所有代码,你可以尝试以下代码片段

class ParentDoc(ABC):
    def __init__(self, ver: int = 1) -> None:
        self.ver = ver

    def to_dict(self) -> dict:
        """Converts the object to a dictionary.

        Returns:
            dict: A dictionary representation of the object.
        """
        data = self.__dict__.copy()
        data["_class"] = self.__class__.__name__
        return data

    @classmethod
    def from_dict(cls, data: dict) -> "ParentDoc":
        """Creates an instance from a dictionary.

        Args:
            data (dict): A dictionary representation of the object.

        Returns:
            ParentDoc: An instance of the class.
        """
        return cls(
            ver=data.get("ver", 1),
        )

    def to_file(self, file_path: str | Path) -> None:
        """Writes the object to a file.

        Args:
            file_path (str | Path): The file path.
        """
        with open(file_path, "w", encoding="utf8") as json_file:
            json.dump(self.to_dict(), json_file)

    @classmethod
    def from_file(cls: type["ParentDoc"], file_path: str | Path) -> "ParentDoc":
        """Creates an instance from a file.

        Args:
            file_path (str | Path): The file path.

        Returns:
            ParentDoc: An instance of the class.
        """
        with open(file_path, "r", encoding="utf8") as json_file:
            data = create_from_dict(json.load(json_file))
        return data


def create_from_dict(data: dict) -> ParentDoc:
    """Creates an object from a dictionary based on the _class field.

    Args:
        data (dict): A dictionary representation of the object.

    Returns:
        ParentDoc: An instance of the appropriate class.
    """
    class_name = data.get("_class")
    if class_name == "ParentDoc":
        return ParentDoc.from_dict(data)
    elif class_name == "ChildDoc":
        return ChildDoc.from_dict(data)
    elif class_name == "Child2Doc":
        return Child2Doc.from_dict(data)
    else:
        raise ValueError(f"Unsupported class: {class_name}")


class ChildDoc(ParentDoc):
    """Base class for a generic child.

    Attributes:
        name (str): The name of the document.
        ver (int): The version of the document.
    """

    def __init__(self, name: str, ver: int = 1) -> None:
        super().__init__(ver=ver)
        self.name = name

    def to_dict(self) -> dict:
        """Converts the object to a dictionary"""
        data = super().to_dict()
        data.update(
            {
                "name": self.name,
            }
        )
        return data

    @classmethod
    def from_dict(cls, data: dict) -> "ChildDoc":
        """Creates an instance from a dictionary.

        Args:
            data (dict): A dictionary representation of the object.

        Returns:
            ChildDoc: An instance of the class.
        """
        return cls(
            name=data["name"],
        )

驱动程序代码示例:

doc = ChildDoc(name="Test Name")
doc.to_file("child_doc.json")

new_doc = ChildDoc.from_file("child_doc.json")


print(type(new_doc))
print(new_doc.name)

输出:

<class '__main__.ChildDoc'>
Test Name
© www.soinside.com 2019 - 2024. All rights reserved.