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
現在、これらの実装は両方とも正常に機能しています。ただし、両方を保持したい-最初のものは開発に役立ちます-しかし、厄介なことは、明らかに2つの間にかなりの量の繰り返しがあることです。それらの__init__()
機能は同一です。どちらにもto_disk()
、インスタンスをファイルに保存して返すメソッドがありますNone
。どちらもfrom_disk()
、ディスクのどこかに保存されているクラスのインスタンスを返す classmethod を持っています。
理想的には、それらの両方を基本クラスから継承させたいと考えています。これにより、さまざまな機能が渡され、完全な実装を提供するためにメソッドとメソッドをオーバーライドする必要があるdict
ことも指定されます。to_disk()
from_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
(唯一の基本クラスとしてではなく) ミックスインとして使用すると、@abstractmethod
デコレーターが混乱するように見えます。そのため、継承されたクラスは、必要な抽象メソッドの実装に失敗した場合に例外を発生させなくなります。
理想的には、基本クラスが標準の Python のすべての機能を継承するようにしたいのですが、dict
インスタンスをインスタンス化するために継承されたクラスに特定のメソッドを実装する必要があることも指定します。
私がやろうとしていることは可能ですか、それとも私のアプローチは根本的に間違っていますか?
(余談ですがABC
、Web アプリなどのデータ構造をキャッシュする最良の方法よりも、 s の動作方法に関心があります。データをキャッシュするより良い方法があると確信していますが、これは私の最初の Flask プロジェクトであり、現時点では私の方法はうまく機能しています。)