Reputation: 51
I have a publisher that can publish messages of a certain type:
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)
This works, and the publish function has the correct type hint, but I want to be able to access the type of the publisher with a get_type()
function.
A simple way to do this is to pass the message type into the constructor:
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)
But this requires writing int
twice in the line p = Publisher[int](int, "chatter")
which seems a bit clumsy and redundant.
I tried wrapping the creation of the publisher in a function so that you don't have to write int
twice, but I get a problem:
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
So what I need, is a method to get convert Type[x]
into x
in a typehint context. Essentially the inverse of what Type
does.
e.g, the last example would become:
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)
But I do not know how to make the InstanceOf
generic.
Is there anyway I can do this? Or any other way to get the functionality I want without having to write int
twice in the line p = Publisher[int](int, "chatter")
edit
Here is another attempt that also doesn't work, but should clarify what I'm trying to do:
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())
In this example p.get_type()
returns ~T
instead of int
Upvotes: 2
Views: 1484
Reputation: 52139
Pass in the type explicitly, but annotate it as Type[T]
. This allows inference of T
without having to specify it, making it enough to specify the type only once (as the argument).
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*]'
Upvotes: 3
Reputation: 51
I have an answer, based on this answer, but it depends on undocumented implementation details.
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())
Upvotes: 0