【问题标题】:Python Typing : Create a class function with a generic type, and also access that type through the classPython Typing:创建具有泛型类型的类函数,并通过类访问该类型
【发布时间】:2020-12-12 14:21:11
【问题描述】:

我有一个可以发布某种类型消息的发布者:

T = TypeVar('T')
class Publisher(Generic[T]):

    def __init__(self, topic: str) -> None:
        self.__topic = topic

    def publish(self, msg: T):
        pass

# As an example, create a publisher that can publish ints
p = Publisher[int]("chatter")
p.publish(1)

这可行,并且发布函数具有正确的类型提示,但我希望能够使用 get_type() 函数访问发布者的类型。

一个简单的方法是将消息类型传递给构造函数:

T = TypeVar('T')
class Publisher(Generic[T]):

    def __init__(self, msg_type: type, topic: str) -> None:
        self.__msg_type = msg_type
        self.__topic = topic

    def publish(self, msg: T):
        pass

    def get_type(self) -> type:
        return self.__msg_type

p = Publisher[int](int, "chatter")
p.publish(1)

但这需要在p = Publisher[int](int, "chatter") 行中写两次int,这似乎有点笨拙和多余。

我尝试将发布者的创建包装在一个函数中,这样您就不必写两次int,但我遇到了一个问题:

T = TypeVar('T', bound=type)
class Publisher(Generic[T]):

    def __init__(self, msg_type: type, topic: str) -> None:
        self.__msg_type = msg_type
        self.__topic = topic

    def publish(self, msg: T):
        pass

    def get_type(self) -> type:
        return self.__msg_type

def create_publisher(msg_type: T, topic: str) -> Publisher[T]:
    return Publisher[T](msg_type, topic)

p = create_publisher(int, "hello")

p.publish(1) #Fails because its expecting Type[int], not an instance of an int

所以我需要的是一种在类型提示上下文中将Type[x] 转换为x 的方法。基本上与Type 的作用相反。

例如,最后一个示例将变为:

T = TypeVar('T', bound=type)
class Publisher(Generic[T]):

    def __init__(self, msg_type: type, topic: str) -> None:
        self.__msg_type = msg_type
        self.__topic = topic

    def publish(self, msg: InstanceOf[T]):
        pass

    def get_type(self) -> type:
        return self.__msg_type

def create_publisher(msg_type: T, topic: str) -> Publisher[T]:
    return Publisher[T](msg_type, topic)

p = create_publisher(int, "hello")

p.publish(1)

但我不知道如何使InstanceOf 通用。

无论如何我可以做到这一点吗?或任何其他方式来获得我想要的功能,而无需在p = Publisher[int](int, "chatter") 行中写两次int

编辑

这是另一个不起作用的尝试,但应该澄清我正在尝试做的事情:

T = TypeVar('T')
class Publisher(Generic[T]):

    def __init__(self, topic: str) -> None:
        self.__topic = topic

    def publish(self, msg: T):
        pass

    def get_type(self) -> type:
        return get_args(Publisher[T])[0]

#This works
print(get_args(Publisher[int])[0])

#This doesn't
p = Publisher[int]("hello")
print(p.get_type())

在此示例中,p.get_type() 返回 ~T 而不是 int

【问题讨论】:

  • 您能否解释一下为什么需要[int] 部分。这似乎是多余的,没有它就可以编写代码。
  • @venky__ 我不确定我是否理解。 int 只是一个例子。可以使用任何类型创建发布者。这能回答你的问题吗?
  • 不,我的意思是Publisher[int](int, "chatter") 可以写成Publisher (int, "chatter")。如果没有那为什么?
  • @venky__ 我想在运行前使用mypy 捕获类型错误。 Publisher[int][int] 确保从现在开始,这个发布者 publish 函数只能用整数调用。如果没有这个,我将不得不在发布函数中进行 runtme 检查,以检查它是否以正确的类型被调用。

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


【解决方案1】:

显式传入类型,但将其注释为Type[T]。这允许在无需指定 T 的情况下进行推断,因此只需指定一次类型(作为参数)就足够了。

class Publisher(Generic[T]):
    # knowing `msg_type` defines `T`
    def __init__(self, msg_type: Type[T], topic: str) -> None:
        self._msg_type = msg_type
        self._topic = topic

    def publish(self, msg: T):
        pass

    def get_type(self) -> Type[T]:
        return self._msg_type


# argument of `int` implies T = int
p = Publisher(int, "hello")
print(p.get_type())  # <class 'int'>
if TYPE_CHECKING:
    reveal_type(p)   # note: Revealed type is 'aaa_testbed.Publisher[builtins.int*]'

【讨论】:

    【解决方案2】:

    我有一个基于 this answer 的答案,但这取决于未记录的实施细节。

    from typing import TypeVar, Generic, get_args, get_origin
    
    T = TypeVar('T')
    
    class Publisher(Generic[T]):
    
        def __init__(self, topic: str) -> None:
            self.__topic = topic
    
        def publish(self, msg: T):
            pass
    
        def get_type(self) -> type:
            return get_args(self.__orig_class__)[0]
    
    p = Publisher[int]("hello")
    print(p.get_type())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 2021-04-09
      • 2013-11-22
      相关资源
      最近更新 更多