实例化一个类型,该类型是TypeVar。

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

作为 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无法实例化实际上是TypeVars的类(TPopMember)。我只想创建一个列表(Population),其中包括一些默认初始化的(在Python中如何说?)TPopMembers。我该如何做?

我正在使用Python 3.7.2。


请记住,这些仅仅是提示。我建议创建一个名为 TPopMember 的新class - Uli Sotschok
你可以传递一个 TPopMember 工厂给 EvolutionaryAlgorithm - Patrick Haugh
4个回答

18

您可能没有意识到类型提示只是一个提示。换句话说,不要认为它是一种类型。您无法实例化它们。

从您的评论中我理解到,您想做的是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. 我的代码检查器提示:Value 'Generic' is unsubscriptablepylint(unsubscriptable-object) 但我不明白为什么。我基本上正在按照这个做:https://docs.python.org/3/library/typing.html#user-defined-generic-types。(至少我看不出区别)。
  2. 好的,我明白了,但泛型的重点在于客户端代码可以决定 TPopMember 应该是什么类型。有没有 Pythonic 的方法可以实现这一点(除了定义一个 IPopMember 接口类)?
- Jupiter
1
重写答案 - adrtam
2
不需要传递 PopMember 的实例到 __init__,只是为了访问它的 __class__ 属性。定义 __init__(self, member_class: Type[PopMember], ...),然后传递 PopMember 本身(或任何 PopMember 的子类)。 - chepner

3
您可以做以下事情:
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)

在这里,可以找到用于定义泛型类的类型实例:self.__orig_class__.__args__[0]

对于classmethods,只需使用此代码 -> cls.__args__[0]


__orig_class__ 是一个内部属性,不应直接使用。 - demberto
@demberto 我知道,看看我下面提供的替代响应... - Marcus

2
另一种避免在序列化类(即使用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内部。
这里的主要优点是您可以重复使用classmethod函数。

0
一个更简单的解决方案是在枚举工厂中使用functools.partial
import random
from typing import Any
from enum import Enum
from functools import partial

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 "Pap({})".format(self.x)

class EvolutionaryAlgorithm:
    def __init__(self, member_type: Any, populationSize: int) -> None:
        self.__member_type = member_type
        self.__population = [self.__member_type() for _ in  range(populationSize)]

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

class EvolutionaryAlgorithmFactory(Enum):
    POP_MEMBER = partial(EvolutionaryAlgorithm, PopMember)
    PAP_MEMBER = partial(EvolutionaryAlgorithm, PapMember)

    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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接