來自 C# 背景并了解其泛型方法,我現在正在嘗試在 Python 中實作類似的東西。我需要以特殊的字串格式序列化和反序列化類,因此我創建了以下兩個基類,第一個用于單個物體序列化,第二個用于該物體型別的串列序列化。
from typing import Any, TypeVar, List, cast, Type, Generic, NewType
import re
T = TypeVar('T')
class Serializable(Generic[T]):
def to_str(self) -> str:
raise NotImplementedError
@classmethod
def from_str(cls, str: str):
raise NotImplementedError
class SerializableList(List[Serializable[T]]):
def __init__(self):
self.separator: str = "\n"
@classmethod
def from_str(cls, str: str):
list = cls()
for match in re.finditer(list.separator, str):
list.append(T().from_str(match)) # <-- PROBLEM: HOW TO INIT A GENERIC ENTITY ???
# list.append(Serializable[T].from_str(match)) <-- Uses base class (NotImplemented) instead of derived class
return list
def to_str(self) -> str:
str = ""
for e in self:
str = str f"{e.to_str()}{self.separator}"
return str
然后我可以從這些類派生,并且必須實作to_str和from_str。請查看標記<-- PROBLEM"。我不知道如何為串列初始化當前使用型別的新物體。我們如何以 Python 方式執行此操作?
uj5u.com熱心網友回復:
正如@user2357112supportsMonica 在評論中所說,typing.Generic幾乎只用于靜態分析,并且在幾乎所有情況下在運行時基本上沒有影響。從你的代碼看,它看起來像你(檔案做什么可能更適合于抽象基類在這里,教程這里),它可以與容易地組合Generic。
具有ABCMeta元類的類被標記為抽象基類 (ABC)。ABC 的子類不能被實體化,除非 ABC 中用@abstractmethod裝飾器標記的所有方法都已被覆寫。在下面我建議的代碼中,我已將ABCMeta元類顯式添加到您的Serializable類中,并SerializableList通過讓它繼承自collections.UserList而不是typing.List. (collections.UserList已經ABCMeta作為它的元類。)
使用 ABC,您可以定義一些這樣的介面(由于抽象方法,您將無法實體化這些介面):
### ABSTRACT INTERFACES ###
from abc import ABCMeta, abstractmethod
from typing import Any, TypeVar, Type, Generic
from collections import UserList
import re
T = TypeVar('T')
class AbstractSerializable(metaclass=ABCMeta):
@abstractmethod
def to_str(self) -> str: ...
@classmethod
@abstractmethod
def from_str(cls: Type[T], string: str) -> T: ...
S = TypeVar('S', bound=AbstractSerializable)
class AbstractSerializableList(UserList[S]):
separator = '\n'
@classmethod
@property
@abstractmethod
def element_cls(cls) -> Type[S]: ...
@classmethod
def from_str(cls, string: str):
new_list = cls()
for match in re.finditer(cls.separator, string):
new_list.append(cls.element_cls.from_str(match))
return new_list
def to_str(self) -> str:
return self.separator.join(e.to_str() for e in self)
然后,您可以提供一些具體的實作,如下所示:
class ConcreteSerializable(AbstractSerializable):
def to_str(self) -> str:
# put your actual implementation here
@classmethod
def from_str(cls: Type[T], string: str) -> T:
# put your actual implementation here
class ConcreteSerializableList(AbstractSerializableList[ConcreteSerializable]:
# this overrides the abstract classmethod-property in the base class
element_cls = ConcreteSerializable
(順便說一句——我改變了你的幾個變數名—— str,,list等等——因為它們隱藏了內置型別和/或函式。這通常會導致煩人的錯誤,即使沒有,也會讓其他人感到困惑閱讀您的代碼!我還清理了您的to_str方法,它可以簡化為單行代碼,并將您的separator變數移動為類變數,因為它對于所有類實體似乎都是相同的,并且似乎永遠不會改變.)
uj5u.com熱心網友回復:
現在我找到了一個骯臟的解決方案 - 這是添加串列條目的型別(建構式)引數,如下所示:
class SerializableList(List[Serializable[T]]):
# This one
# |
# v
def __init__(self, separator: str = "\n", entity_class: Type = None):
self.separator = separator
self.entity_class = entity_class
@classmethod
def from_str(cls, str: str):
list = cls()
for match in re.finditer(list.separator, str):
list.append(list.entity_class.from_str(match))
return list
我想知道是否有更簡潔的方法可以從 List[T] 獲取正確的 [T] 型別建構式,因為它已經在那里提供了?
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/311198.html
上一篇:增加沒有型別的陣列引數的方法
下一篇:如何使通用回呼函式做出反應
