gpt4 book ai didi

python - 重写方法签名时如何避免违反里氏替换原则

转载 作者:行者123 更新时间:2023-12-02 01:38:46 25 4
gpt4 key购买 nike

我有一个抽象类DataWriter,它定义了一个抽象方法write()。该类应该是一组动态具体类的基类,其中每个类都旨在实现其自己的方法 write() 版本。为了定义方法write()的参数meta的数据类型,我创建了类型WriterMeta,如下所示:

WriterMeta = Typing.Union[GSheetWritable, S3Writable, LocalWritable]

每个具体类将负责处理符合联合的不同类型之一,但 linter mypy 似乎没有掌握这一点,因为当我定义方法的签名时使用联合类型之一作为参数 meta 的具体类的 write() ,它标志着违反 Liskov 替换原则,我对此相信不存在,因为具体类是抽象类的子集,这意味着父类可以毫无问题地替换子类。

这是我的代码:

class LocalWritable(typing.TypedDict):
file_name: str


class GSheetWritable(typing.TypedDict):
tab_name: str


class S3Writable(typing.TypedDict):
data_name: str
table_name: str


WriterMeta = typing.Union[GSheetWritable, S3Writable, LocalWritable]

class GSheetOutputWriter(DataWriter):
def __init__(
self, google_driver: GoogleApiDriver, folder: str, settings, timestamp, env
):
self._connector = google_driver
self.folder = folder
self.settings = settings
self.timestamp = timestamp
self.env = env
self.file_name = self.get_file_name()
self._target = self.create_gsheet()
self.new = True

def get_file_name(self) -> str:
file_name = (
"boxes_shipping_costs_"
+ self.settings["volume_source"]
+ "_"
+ (
self.timestamp
if self.settings["volume_source"] == "adhoc"
else self.settings["scm_week"]
)
)

return file_name

def create_gsheet(self):
gsheet = self.connector.sheet_create(self.file_name, folder_id=self.folder)
gsheet.worksheet("Sheet1").resize(rows=1, cols=1)

return gsheet

@property
def connector(self) -> typing.Any:
return self._connector

@property
def target(self) -> typing.Any:
return self._target

def write(self, data: pd.DataFrame, meta: GSheetWritable, versionize: bool):
data = data.replace({np.nan: 0, np.Inf: "Inf"})

print("Writing '{}' table to gsheet.".format(meta["tab_name"]))
if self.new:
tab = self.connector.get_worksheet(self.target.url, "Sheet1")
self.connector.rename_worksheet(tab, meta["tab_name"])
self.new = False
else:
tab = self.connector.add_worksheet(
self.target, meta["tab_name"], rows=1, cols=1
)

time.sleep(random.randint(30, 60))
self.connector.update_worksheet(
tab, [data.columns.values.tolist()] + data.values.tolist()
)

我对里氏替代原理的理解正确吗?如何重构这组类以使 mypy 接受它们?

最佳答案

为什么失败

当您声明对某种类型T(在您的情况下是Union)进行操作的抽象类方法时,从 mypy 的角度来看,它意味着以下内容:每个 子类必须实现此方法,该方法接受类型 T 或更宽的类型。仅接受 T 的一部分的方法违反了 LSP:每当您引用抽象类时,您都认为 T 类型的所有内容都可以在该方法中使用,而具体实现则不然不允许这样做。

类型安全方式 - 通用解决方案

您可以为此使用泛型类。

import pandas as pd
from abc import ABC, abstractmethod
from typing import Generic, TypeVar, TypedDict

class LocalWritable(TypedDict):
file_name: str

class GSheetWritable(TypedDict):
tab_name: str

class S3Writable(TypedDict):
data_name: str
table_name: str

# This variable is compatible with any of classes or their subclasses,
# not with `Union` of them as opposed to TypeVar(bound=Union[...]) usage.
_OutputT = TypeVar('_OutputT', GSheetWritable, S3Writable, LocalWritable)

class DataWriter(ABC, Generic[_OutputT]):
@abstractmethod
def write(self, data: pd.DataFrame, meta: _OutputT, versionize: bool) -> None: ...

class GSheetDataWriter(DataWriter[GSheetWritable]):
def write(self, data: pd.DataFrame, meta: GSheetWritable, versionize: bool) -> None:
pass
# Implementation goes here

关于python - 重写方法签名时如何避免违反里氏替换原则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71932334/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com