I am new to python and am coming from C# and java. I want to instantiate a class of the type provided as type hint R
as following
from typing import (TypeVar, Generic)
class BaseParams(object):
def __init__(self) -> None:
self.name = 'set-in-base-class'
class ChildParams(BaseParams):
def __init__(self) -> None:
super().__init__()
self.name = 'set-in-child-class'
R = TypeVar('R', bound= BaseParams)
class MyGeneric(Generic[R]):
def __init__(self) -> None:
super().__init__()
def test(self):
r = R() # how should I instantiate R here
print(r.name)
c = MyGeneric[ChildParams]()
c.test()
something like the following C# code
class BaseParams
{
public BaseParams()
{
Name = "set-in-base-class";
}
public string Name { get; set; }
}
class ChildParams : BaseParams
{
public ChildParams()
{
Name = "set-in-child-class";
}
}
class MyGenericClass<R> where R : BaseParams, new()
{
public void test()
{
var r = new R();
Console.WriteLine(r.Name);
}
}
I've made quite a lot search on how to do that in python and all the sources refer to a situation where we provide the type in a method or something like that. I wonder if it is possible to do that at all.
would you please someone help me to have a workaround on this?
It's still type hints and not type declaration, but I like inspecting weird objects and looking at their insides, so I gave it a go:
I tried to inspect Generic
s in interactive session using dir
and also looked for clues in the typing
's source.
My testing consisted of having one MyGeneric without R specified and one with, then inspecting them both. Both of them had __orig_bases__
which included Generic[R]
, but then I noticed that the parametrized MyClass has additional __orig_class__
attribute - bingo!
>>> a = MyGeneric()
>>> b = MyGeneric[int]()
>>> a.__orig_bases__
(typing.Generic[~R],)
>>> b.__orig_
b.__orig_bases__ b.__orig_class__(
>>> b.__orig_bases__
(typing.Generic[~R],)
>>> a.__orig_class__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyGeneric' object has no attribute '__orig_class__'. Did you mean: '__orig_bases__'?
>>> b.__orig_class__
__main__.MyGeneric[int]
By looking at Generic's source earlier, I already knew that .__args__
parameter gets whatever is in [], as tuple (because it could be multiple things).
So:
>>> b.__orig_class__.__args__
(<class 'int'>,)
>>> b.__orig_class__.__args__[0]
<class 'int'>
Going back to your code I had in file, I put that attribute chain and run the file - and got set-in-child-class
.
from typing import (TypeVar, Generic)
class BaseParams(object):
def __init__(self) -> None:
self.name = 'set-in-base-class'
class ChildParams(BaseParams):
def __init__(self) -> None:
super().__init__()
self.name = 'set-in-child-class'
R = TypeVar('R', bound= BaseParams)
class MyGeneric(Generic[R]):
def __init__(self) -> None:
super().__init__()
def test(self):
r = self.__orig_class__.__args__[0]()
print(r.name)
c = MyGeneric[ChildParams]()
c.test()
I'd advise to put that magical chain of attributes as some function with nice name. Like get_parametrized_base_arg
or something. Or just put a good comment to explain the magic.