Generic[T] classmethod implicit type retrieval in Python 3.12

I would like to use the new Python 3.12 generic type signature syntax to know the type of an about-to-be-instantiated class within the classmethod of that class.

For example, I would like to print the concrete type of T in this example:

class MyClass[T]:
    kind: type[T]

    ...

    @classmethod
    def make_class[T](cls) -> "MyClass[T]":
        print("I am type {T}!")
        return cls()

I’ve used the advice in the following StackOverflow question to do this for reifying the type within both __new__ and __init__ but I have yet to to figure out a clever way to do this in either a static or class method (ideally a class method).

  • Generic[T] base class – how to get type of T from within instance?

My goal is to have the following API:

>>> MyClass[int].make_class()
"I am type int!"

Or this API (which I don’t think is syntactically possible yet):

>>> MyClass.make_class[int]()
"I am type int!"

Where in either case, the returned instance would have int bound to the class variable so I can use it later.

MyClass[int].make_class().kind is int == True

I am open to “hacks” (including heavy use of inspect).

If you read the source code of typing.py, you’ll see that the type arguments for a Generic is stored as the __args__ attribute of the base class _BaseGenericAlias. Note that this is an undocumented implementation detail.

We then just need to patch its proxy attribute getter _BaseGenericAlias.__getattr__ to inject an additional keyword argument into the method call with a wrapper function when the method is decorated with a classmethod subclass that marks the function with a special attribute:

class TypeArgsClassMethod(classmethod):
    def __get__(self, obj, obj_type=None):
        method = super().__get__(obj, obj_type)
        method.__func__._inject_type_args = True
        return method

def __getattr__(self, name):
    if hasattr(obj := orig_getattr(self, name), '_inject_type_args'):
        @wraps(obj)
        def wrapper(*args, **kwargs):
            return obj(*args, __type_args__=self.__args__, **kwargs)
        return wrapper
    return obj

if 'orig_getattr' not in globals():
    orig_getattr = _BaseGenericAlias.__getattr__
    _BaseGenericAlias.__getattr__ = __getattr__

so that:

class MyClass[T]:
    kind: type[T]

    @TypeArgsClassMethod
    def make_class(cls, __type_args__) -> "MyClass[T]":
        print(__type_args__)
        return cls()

MyClass[int].make_class()

outputs:

(<class 'int'>,)

Demo here

Alternatively, you can rebind the custom class method to the Generic type, so that the first argument of the class method would become the Generic type. The built-in types.MethodType does not allow __self__ to be updated for a rebind so you would have to define a Python-equivalent version:

from functools import update_wrapper
from typing import _BaseGenericAlias

class MethodType:
    def __init__(self, func, obj):
        self.__func__ = func
        self.__self__ = obj

    def __call__(self, *args, **kwargs):
        func = self.__func__
        obj = self.__self__
        return func(obj, *args, **kwargs)

class GenericClassMethod:
    def __init__(self, f):
        self.f = f
        update_wrapper(self, f)

    def __get__(self, obj, cls=None):
        if cls is None:
            cls = type(obj)
        method = MethodType(self.f, cls)
        method._generic_classmethod = True
        return method

def __getattr__(self, name):
    if hasattr(obj := orig_getattr(self, name), '_generic_classmethod'):
        obj.__self__ = self
    return obj

if 'orig_getattr' not in globals():
    orig_getattr = _BaseGenericAlias.__getattr__
    _BaseGenericAlias.__getattr__ = __getattr__

so that you can access its __args__ attribute for the type arguments, and its __origin__ attribute for the original class:

class MyClass[T]:
    kind: type[T]

    @GenericClassMethod
    def make_class(cls) -> "MyClass[T]":
        print(cls)
        print(cls.__origin__)
        print(cls.__args__)

MyClass[int].make_class()

This outputs:

__main__.MyClass[int]
<class '__main__.MyClass'>
(<class 'int'>,)

Demo here

Finally, an even more seamless approach that would achieve the behavior of substiting the type variable T with the actual argument as suggested in your question would be to map the type variables the in cell content of the class method’s function closure to the corresponding type arguments.

This approach has the benefit of avoiding any alteration to the class method’s usage (with no injection of an argument and no change to the nature of the cls argument) so you can avoid having to define a custom classmethod descriptor. To identify a class method you can use the isclassmethod function offered in this answer:

from typing import _BaseGenericAlias
from types import FunctionType, CellType
from collections.abc import Hashable
from operator import attrgetter

def __getattr__(self, name):
    if (isclassmethod(obj := orig_getattr(self, name)) and
            (func := obj.__func__).__closure__):
        param_to_arg = dict(zip(self.__origin__.__parameters__, self.__args__))
        return FunctionType(
            func.__code__,
            func.__globals__,
            func.__name__,
            func.__defaults__,
            tuple(
                CellType(
                    param_to_arg.get(value, value)
                    if isinstance(value, Hashable) else value
                )
                for value in map(attrgetter('cell_contents'), func.__closure__)
            )
        ).__get__(obj.__self__)
    return obj

if 'orig_getattr' not in globals():
    orig_getattr = _BaseGenericAlias.__getattr__
    _BaseGenericAlias.__getattr__ = __getattr__

so that:

class MyClass[T]:
    @classmethod
    def make_class(cls):
        print(T)
    
MyClass[int].make_class()

outputs:

<class 'int'>

Demo here

8

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật