python - 是否可以将抽象基类用作带有 dict 子类的 mixin?
问题描述
TL;DR:有兴趣了解是否可以按照我的意愿使用抽象基类作为混合,或者我的方法是否从根本上被误导。
我有一个我一直在做的 Flask 项目。作为我项目的一部分,我实现了一个RememberingDict
类。它是 的一个简单的子类dict
,添加了一些额外的功能:它会记住它的创建时间,它知道如何将自己腌制/保存到磁盘,并且它知道如何从磁盘打开/取消腌制自己:
from __future__ import annotations
import pickle
from datetime import datetime
from typing import Final, Optional, TypeVar, Any, Hashable
FILE_PATH: Final = 'data.pickle'
T = TypeVar('T', bound='RememberingDict')
class RememberingDict(dict):
def __init__(self, data: Optional[dict[Hashable, Any]] = None) -> None:
super().__init__(data if data is not None else {})
self.creation_time: datetime = datetime.now()
def to_disk(self) -> None:
"""I save a copy of the data to a file"""
with open(FILE_PATH, 'wb') as f:
pickle.dump(self, f)
@classmethod
def from_disk(cls: type[T]) -> T:
"""I extract a copy of the data from a file"""
with open(FILE_PATH, 'rb') as f:
latest_dataset: T = pickle.load(f)
return latest_dataset
该代码在本地开发服务器上非常适合我的目的,所以一切都很好,但是(由于这里没有必要进入的原因),在 Google App Engine 上部署它时它不起作用,所以出于这些目的,我设计了这个替代实现:
from __future__ import annotations
import pickle
from datetime import datetime
from typing import Optional, TypeVar, Hashable, Any
from google.cloud.storage.blob import Blob
def get_google_blob() -> Blob:
"""
Actual implementation unnecessary to go into,
but rest assured that the real version of this function returns a Blob object,
linked to Google Storage account credentials,
from which files can be uploaded to, and downloaded from,
Google's Cloud Storage platform.
"""
pass
T = TypeVar('T', bound='RememberingDict')
class RememberingDict(dict):
def __init__(self, data: Optional[dict[Hashable, Any]] = None) -> None:
super().__init__(data if data is not None else {})
self.creation_time: datetime = datetime.now()
def to_disk(self) -> None:
"""I upload a copy of the data to Google's Cloud Storage"""
get_google_blob().upload_from_string(pickle.dumps(self))
@classmethod
def from_disk(cls: type[T]) -> T:
"""I extract a copy of the data from Google's Cloud Storage"""
latest dataset: T = pickle.loads(get_google_blob().download_as_bytes())
return latest_dataset
现在,这两种实现都可以正常工作。然而,我想保留它们——第一个对开发很有用——但令人讨厌的是,两者之间显然有相当多的重复。它们的__init__()
功能是相同的;它们都有一个to_disk()
将实例保存到文件并返回的方法None
;并且它们都有一个from_disk()
类方法,该方法返回一个已保存到某个磁盘的类的实例。
理想情况下,我希望它们都继承自一个基类,从而传递给它们各种类似的dict
能力,并指定必须重写to_disk()
andfrom_disk()
方法以提供完整的实现。
这感觉像是一个ABC
s应该能够解决的问题。我尝试了以下方法:
from __future__ import annotations
from datetime import datetime
from typing import Final, Optional, TypeVar, Hashable, Any
from abc import ABC, abstractmethod
from google.cloud.storage.blob import Blob
T = TypeVar('T', bound='AbstractRememberingDict')
class AbstractRememberingDict(ABC, dict):
def __init__(self, data: Optional[dict[Hashable, Any]] = None) -> None:
super().__init__(data if data is not None else {})
self.creation_time: datetime = datetime.now()
@abstractmethod
def to_disk(self) -> None: ...
@classmethod
@abstractmethod
def from_disk(cls: type[T]) -> T: ...
FILE_PATH: Final = 'data.pickle'
class LocalRememberingDict(AbstractRememberingDict):
def to_disk(self) -> None:
"""I save a copy of the data to a file"""
with open(FILE_PATH, 'wb') as f:
pickle.dump(self, f)
@classmethod
def from_disk(cls: type[T]) -> T:
"""I extract a copy of the data from a file"""
with open(FILE_PATH, 'rb') as f:
latest_dataset: T = pickle.load(f)
return latest_dataset
def get_google_blob() -> Blob:
"""
Actual implementation unnecessary to go into,
but rest assured that the real version of this function returns a Blob object,
linked to Google Storage account credentials,
from which files can be uploaded to, and downloaded from,
Google's Cloud Storage platform.
"""
pass
class RemoteRememberingDict(AbstractRememberingDict):
def to_disk(self) -> None:
"""I upload a copy of the data to Google's Cloud Storage"""
get_google_blob().upload_from_string(pickle.dumps(self))
@classmethod
def from_disk(cls: type[T]) -> T:
"""I extract a copy of the data from Google's Cloud Storage"""
latest_dataset: T = pickle.loads(get_google_blob().download_as_bytes())
return latest_dataset
但是,将ABC
用作 mixin(而不是作为唯一的基类)似乎会弄乱@abstractmethod
装饰器,因此如果继承的类无法实现所需的抽象方法,则不会再引发异常。
理想情况下,我希望我的基类继承标准 Python 的所有功能dict
,但还指定必须在继承的类中实现某些方法才能实例化实例。
我正在尝试做的事情是可能的,还是我的方法从根本上被误导了?
(顺便说一句:我对ABC
s 的工作方式更感兴趣,而不是缓存 Web 应用程序数据结构的最佳方法等。我确信可能有更好的缓存数据方法,但这是我的第一个 Flask 项目,我的方式目前对我来说很有效。)
解决方案
您可以dict
通过子类化collections.UserDict
来解决子类化问题。正如文档所说:
模拟字典的类。实例的内容保存在常规字典中,可通过 UserDict 实例的 data 属性访问。如果提供了initialdata,则使用其内容初始化数据;请注意,不会保留对 initialdata 的引用,允许将其用于其他目的。
本质上,它是一个围绕dict
. 您应该能够将它与多重继承一起用作抽象基类,就像使用AbstractRememberingDict
.
推荐阅读
- python-3.x - Python Dask:无法将非有限值(NA 或 inf)转换为整数
- assembly - 像 mov byte ptr [rax + rdx-1], 00 这样的指令是什么意思,其中 rax 不是指针
- python - 使用 Django + Python 创建 Web 应用程序后,我可以更改模型吗?
- windows - 是否可以只复制新文件?xcopy/robocopy
- entity-framework - DBCommandInterceptor 中的安全声明
- graphql - 如何编写突变解析器来更改对象的一个或多个特定值?
- c# - Firebase 身份验证返回 System.NullReference 异常
- amazon-web-services - codeBuild 容器如何在没有事先身份验证的情况下运行 aws-cli 命令?
- json - 视觉工作室代码-tasks.json-包含路径带有'',无法识别
- three.js - GLTF 模型太暗