首页 > 解决方案 > 提供类型并返回这些类型的对象的 TypeHinting 函数

问题描述

我想在 python 中制作实体组件系统(ECS)。

Entity上课:

from typing import Optional, TypeVar, Type


T = TypeVar('T')


class Entity:
    def __init__(self):
        self.components = []

    def add_component(self, c):
        self.components.append(c)

    def get_first_component(self, Type: Type[T]) -> Optional[T]:
        for c in self.components:
            if isinstance(c, Type):
                return c

    def get_first_components(self, *Types):
        res = []
        for Type in Types:
            res.append(self.get_first_component(Type))
        return res

类型提示get_first_component很容易,但我不明白如何对get_first_components函数进行类型提示。此函数给出类型列表并返回这些类型的对象列表。

例子:

e.get_first_components(Position, Health) # returns [Position(2, 2), Health(10, 10)]

我认为它像:

A = TypeVar('A')
B = TypeVar('B')

def f(Types: [Type[A], Type[B], ...]) -> [A, B, ...]:
    # some code ...

抱歉我的英语不好:(

它需要在系统中进行类型提示:

class MoveSystem(System):
    def __init__(self) -> None:
        pass

    def run_for_entity(self, e: Entity):
        pos, m2 = e.get_first_components(Pos2, Move2)
        if m2.active: # <- no type hinting after typing "m2."
            pos.x += m2.dx
            pos.y += m2.dy
            m2.active = False

标签: pythonpython-3.xgame-enginetype-hinting

解决方案


Python 的类型提示系统无法以您想要的方式描述您的函数。您需要能够描述一系列不同类型的任意长度,然后并行地描述这些类型的对象。不幸的是,目前这是不可能的。

你能做的最好的事情是:

def get_first_components(self, *Types: Type[T]) -> List[Optional[T]]:

但这可能不会做你想要的。将T匹配您传递给函数的类型的公共基类,这可能是object如果您的类没有任何其他公共基类。这意味着,当您将返回的列表解压缩为单独的变量时,类型检查器会将它们全部标识为基类的实例,而不是每个都具有您在相应位置传递的特定类型。

不过,您可以使用其他具有可行类型提示的方法来使您的调用代码正常工作:

pos = e.get_first_component(Pos2)  # will be identified as Optional[Pos2]
m2 = e.get_first_component(Move2)  # will be identified as Optional[Move2]

作为旁注,由于您获得的值为Optional,您可能需要检查它们是否为None。如果您使类型提示起作用,如果您在m2.active没有首先检查的情况下执行类似操作,则会收到警告,因为None没有active属性。


推荐阅读