Python 泛型初始化对象
Python init object of generic type
我有 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 方式?
现在我找到了一个脏解决方案——这是为列表条目添加一个类型(构造函数)参数,如下所示:
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] 类型构造函数,因为那里已经提供了它?
正如@user2357112supportsMonica 在评论中所说,typing.Generic
几乎只用于静态分析,几乎在所有情况下在运行时基本上都没有影响。从代码的外观来看,您正在做的事情似乎更适合 Abstract Base Classes(文档 here, tutorial here),它可以很容易地与 Generic
结合使用。
具有 ABCMeta
作为其元 class 的 class 被标记为抽象基础 Class (ABC)。 ABC 的 subclass 不能实例化,除非 ABC 中标有 @abstractmethod
装饰器的所有方法都已被覆盖。在我下面建议的代码中,我已经明确地将 ABCMeta
metaclass 添加到您的 Serializable
class,并将其隐式添加到您的 SerializableList
[=39] =] 让它继承自 collections.UserList
而不是 typing.List
。 (collections.UserList
已经有 ABCMeta
作为其元 class。)
使用 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
等——因为它们隐藏了内置类型 and/or 函数。这通常会导致烦人的错误,即使没有,也会让阅读您代码的其他人感到困惑!我还清理了您的 to_str
方法,它可以简化为一行,并移动了您的 separator
变量是一个 class 变量,因为它似乎对所有 class 个实例都是相同的,而且似乎从未被改变过。)
我有 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 方式?
现在我找到了一个脏解决方案——这是为列表条目添加一个类型(构造函数)参数,如下所示:
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] 类型构造函数,因为那里已经提供了它?
正如@user2357112supportsMonica 在评论中所说,typing.Generic
几乎只用于静态分析,几乎在所有情况下在运行时基本上都没有影响。从代码的外观来看,您正在做的事情似乎更适合 Abstract Base Classes(文档 here, tutorial here),它可以很容易地与 Generic
结合使用。
具有 ABCMeta
作为其元 class 的 class 被标记为抽象基础 Class (ABC)。 ABC 的 subclass 不能实例化,除非 ABC 中标有 @abstractmethod
装饰器的所有方法都已被覆盖。在我下面建议的代码中,我已经明确地将 ABCMeta
metaclass 添加到您的 Serializable
class,并将其隐式添加到您的 SerializableList
[=39] =] 让它继承自 collections.UserList
而不是 typing.List
。 (collections.UserList
已经有 ABCMeta
作为其元 class。)
使用 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
等——因为它们隐藏了内置类型 and/or 函数。这通常会导致烦人的错误,即使没有,也会让阅读您代码的其他人感到困惑!我还清理了您的 to_str
方法,它可以简化为一行,并移动了您的 separator
变量是一个 class 变量,因为它似乎对所有 class 个实例都是相同的,而且似乎从未被改变过。)