Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the generic class property in python

I have a class like:

class ExampleClass(BaseClass[Someclass]):
  pass


class BaseClass(AbstractBaseClass, Generic[T]):
  pass

I want to be able to do something like ExampleClass.targetType where I will return Someclass.__name__ how can I do this inside BaseClass I cannot seem to do T.__name__

I can workaround by defining a method like

class ExampleClass(BaseClass[Something]):
    version = 1

    def get_target_class_name() -> str:
        return Something.__name__

But I will need to copy this for every class

like image 583
Jiew Meng Avatar asked Jun 19 '26 22:06

Jiew Meng


2 Answers

You can define a metaclass:

from typing import Generic, TypeVar

T = TypeVar("T")

class AbstractBaseClass:
    pass

class BaseClassMeta(type):
    def __init__(cls, name, bases, attrs):
        assert "target_type" not in attrs
        if len(attrs["__orig_bases__"]) == 1:
            base, = attrs["__orig_bases__"]
            if args := getattr(base, "__args__", None):
                cls.target_type = args
        return super().__init__(name, bases, attrs)

class BaseClass(AbstractBaseClass, Generic[T], metaclass=BaseClassMeta):
    pass

class TargetClass():
    pass

class ExampleClass(BaseClass[TargetClass]):
    pass

print(ExampleClass().target_type) # TargetClass

This will make all subclasses of BaseClass that have a single superclass which has a single generic parameter have an attribute target_type, set to that parameter. This will be available to instances of the class too, and subclasses.

like image 175
Hack5 Avatar answered Jun 21 '26 13:06

Hack5


You can make use of typing.get_args and __orig_bases__.

get_args will give you the arguments of a type:

assert get_args(Dict[int, str]) == (int, str)

and __orig_bases__ stores the original bases as a tuple. In the context of the example code below

assert Derived.__orig_bases__ == (Base[int],)

so the resulting code would be something like:

from abc import ABC
from typing import Generic, TypeVar, get_args

T = TypeVar("T")


class Base(ABC, Generic[T]):
    def target_type(self) -> str:
        return get_args(self.__orig_bases__[0])[0].__name__


class Derived(Base[int]):
    pass


d = Derived()
print(d.target_type())
# prints 'int'
like image 38
onepan Avatar answered Jun 21 '26 11:06

onepan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!