How can I access T from a Generic[T] instance early in its lifecycle?

Viewed 356

I know I can do this:

import typing

T = typing.TypeVar("T")

class MyGenericClass(Generic[T]):
    def a_method(self):
        print(self.__orig_class__)

MyOtherGeneric[SomeBaseClass]().a_method()

to print SomeBaseClass. Probably, I will just stick with that ability to achieve what I am ultimately trying to do (modify functionality based on T), but I am now stuck wondering how all of this even works.

Originally, I wanted to access the base type information (the value of T) from inside the class at the time the object is being instantiated, or soon thereafter, rather than later in its lifecycle.

As a concrete example, in the code below, I wanted something to replace any of ?n? so I could get the value SomeOtherBaseClass early in the object's lifecycle. Maybe there's some code that needs to go above one of those lines, as well.

import typing

T = typing.TypeVar("T")

class MyOtherGenericClass(Generic[T]):
    def __init__(self, *args, **kwargs):
        print(?1?)
    
    def __new__(klass, *args, **kwargs):
        print(?2?)

MyOtherGenericClass[SomeOtherBaseClass]()

I was trying to set some instance variables at the time of instantiation (or, somehow, soon after it) based on the value of T. I'm rethinking my approach given that the typing module and, specifically, this stuff with generics, still seems to be in an unstable period of development.

So… Is that possible? A user pointed out that, at least in 3.8, __orig_class__ gets set during typing._GenericAlias.__call__, but how does that __call__ method get invoked? When does that happen?

Related reading:

1 Answers

I dunno, it looks like if you wanna have a Self type, which is a thing in PEP 673 in the upcomming Python 3.11.

The current workaround for the not yet implemented Self-Type is:

from typing import TypeVar

TShape = TypeVar("TShape", bound="Shape")

class Shape:
    def set_scale(self: TShape, scale: float) -> TShape:
        self.scale = scale
        return self


class Circle(Shape):
    def set_radius(self, radius: float) -> Circle:
        self.radius = radius
        return self

Circle().set_scale(0.5).set_radius(2.7)  # => Circle

Whereby the the self parameter and the return type are both hinted with the upper-bounded Type-Variable TShape instead of the Self Type.

Related