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_strfrom_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

(顺便说一下——我更改了你的几个变量名——strlist 等——因为它们隐藏了内置类型 and/or 函数。这通常会导致烦人的错误,即使没有,也会让阅读您代码的其他人感到困惑!我还清理了您的 to_str 方法,它可以简化为一行,并移动了您的 separator 变量是一个 class 变量,因为它似乎对所有 class 个实例都是相同的,而且似乎从未被改变过。)