作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个抽象类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/
我是一名优秀的程序员,十分优秀!