【问题标题】:Instantiate a type that is a TypeVar实例化作为 TypeVar 的类型
【发布时间】:2019-08-16 03:43:29
【问题描述】:

作为一名 C++ 程序员,以下代码对我来说似乎很自然,但它不会运行:

from typing import TypeVar, Generic, List, NewType

TPopMember = TypeVar('TPopMember')
Population = NewType('Population', List[TPopMember])
class EvolutionaryAlgorithm(Generic[TPopMember]):
    def __init__(self, populationSize: int) -> None:
        # The following raises TypeError: 'TypeVar' object is not callable
        self.__population = Population([TPopMember() for _ in range(populationSize)])

显然 Python 无法实例化实际上是 TypeVar 的类(TPopMember)。我只是想创建一个带有几个默认初始化的列表(人口)(你在 Python 中怎么说?)TPopMembers。我该怎么办?

我正在使用 Python 3.7.2。

【问题讨论】:

  • 请记住,这些只是提示。我建议创建一个新的class TPopMember
  • 您可以通过要求将TPopMember 工厂传递给EvolutionaryAlgorithm

标签: python python-3.x type-hinting


【解决方案1】:

你没有意识到类型提示是一个提示。换句话说,根本不认为它是一种类型。你不能实例化它们。

我从您的评论中了解到,您的意图是做 C++ 模板允许您做的事情。所以这是我实现这一目标的方法:

from typing import TypeVar, Generic, List, NewType, Type
import random

class PopMember:
    def __init__(self):
        self.x = random.randint(0, 100)
    def __repr__(self):
        return "Pop({})".format(self.x)

TPopMember = TypeVar("TPopMember")
Population = NewType('Population', List[TPopMember])

class EvolutionaryAlgorithm(Generic[TPopMember]):
    def __init__(self, member_class: Type[TPopMember], populationSize: int) -> None:
        self.__population = Population([member_class() for _ in range(populationSize)])
    def __repr__(self):
        return "EA({})".format(self.__population)

x = EvolutionaryAlgorithm(PopMember, 5)
print(x)

输出:

EA([Pop(49), Pop(94), Pop(24), Pop(73), Pop(66)])

您必须了解的是,如果您从Generic[T] 派生一个类,则需要在创建类时使用T 一些方法。在我的示例中,我 创建了一个虚拟对象并解析其类并启动它。通常我不会这样写,我可以直接抛出一个类作为参数 将一个类传递给构造函数以请求生成这种特定类型的项目,因为类本身与它的实例不同,是也是一个 Python 对象。 (感谢 chepner 的建议)

【讨论】:

  • 1.我的 linter 确实说:Value 'Generic' is unsubscriptablepylint(unsubscriptable-object) 但我不明白为什么。我基本上就是这样做的:docs.python.org/3/library/…。 (至少我看不出有什么区别)。 2. 好的,我理解,但通用的一点是客户端代码可以决定 TPopMember 应该是什么类型。任何pythonic方式来实现这一点(除了定义一个IPopMember接口类)?
  • 重写答案
  • 无需将PopMember 的实例传递给__init__,只需访问其__class__ 属性即可。定义__init__(self, member_class: Type[PopMember], ...),然后传递PopMember 本身(或PopMember 的任何子类)。
【解决方案2】:

您可以执行以下操作:

from typing import TypeVar, Generic, List, NewType
import random

class PopMember:
    def __init__(self):
        self.x = random.randint(0, 100)
    def __repr__(self):
        return "Pop({})".format(self.x)

TPopMember = TypeVar('TPopMember')
Population = NewType('Population', List[TPopMember])
class EvolutionaryAlgorithm(Generic[TPopMember]):
    def __init__(self, populationSize: int) -> None:
        obj = self.__orig_class__.__args__[0]
        self.__population = Population([obj() for _ in  range(populationSize)])

    @property
    def population(self):
        return self.__population

evolution = EvolutionaryAlgorithm[PopMember](100)
print(evolution.population)

可以在此处的实例中找到用于定义 Generic 类的类型:self.__orig_class__.__args__[0]

对于类方法只需使用这个 -> cls.__args__[0]

【讨论】:

    【解决方案3】:

    在序列化类(即使用pickle)时,还有另一种避免问题的可能性。

    您可以执行以下操作,而不是使用 Generic:

    from typing import Callable, Any
    import random
    from enum import Enum
    from functools import wraps
    
    class PopMember:
        def __init__(self):
            self.x = random.randint(0, 100)
        def __repr__(self):
            return "Pop({})".format(self.x)
    
    class PapMember:
        def __init__(self):
            self.x = random.randint(0, 200)
        def __repr__(self):
            return "Pop({})".format(self.x)
    
    
    def check_type(func: Callable) -> Callable:
        """Decorator to check that the child class has defined the POINT_TYPE member attribute."""
        @wraps(func)
        def wrapper(obj, *args, **kwargs) -> Any:
            if not hasattr(obj, 'T'):
                raise NotImplementedError(
                    "You can not instantiate an abstract class.")
            return func(obj, *args, **kwargs)
        return wrapper
    
    class EvolutionaryAlgorithm:
        @check_type
        def __init__(self, populationSize: int) -> None:
            self.__population = [self.T() for _ in  range(populationSize)]
    
        @classmethod
        @check_type
        def create(cls, populationSize: int):
            """Example of classmethod."""
            # You can use T as cls.T
            return cls(populationSize=populationSize)
    
        @property
        def population(self):
            return self.__population
    
    class EvolutionaryAlgorithmPopMember(EvolutionaryAlgorithm):
        T = PopMember
    
    class EvolutionaryAlgorithmPapMember(EvolutionaryAlgorithm):
        T = PapMember
    
    class EvolutionaryAlgorithmFactory(Enum):
        POP_MEMBER = EvolutionaryAlgorithmPopMember
        PAP_MEMBER = EvolutionaryAlgorithmPapMember
    
        def __call__(self, *args, **kwargs) -> Any:
            return self.value(*args, **kwargs)
    
        def __str__(self) -> str:
            return self.name
    
    
    evolution = EvolutionaryAlgorithmFactory.POP_MEMBER(100)
    print(evolution.population)
    

    这将避免很多问题,而不是破解 python 内部。

    这里的主要优点是您可以重用类方法函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多