我有一个父类,以及许多从父类继承的子类。这些类本质上只是 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
但没有成功。
要为
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