Home > Net >  Implement GenericAlias in a user-defined class (Python)
Implement GenericAlias in a user-defined class (Python)

Time:01-04

From PEP 585, you can specify, for example, the type of the elements in a list:

arr: list[int] = [1, 2, 3]

list[int] is of type GenericAlias, and is equivalent to GenericAlias(list, int). I would like to implement this functionality in a custom class. For example:

>>> MyClass[int]
MyClass[int]
>>> x: MyClass[int] = MyClass(42)

If I create a class that inherits from list, it works, so I guess it's due to an attribute.

I already tried class methods or static methods:

class MyClass:
    @classmethod
    def __getitem__(cls, Class: type):
        return GenericAlias(cls, Class)

But it didn't work. I get the error TypeError: 'type' object is not subscriptable.

CodePudding user response:

I would like to implement this functionality in a custom class.

You did not explain what exactly you mean by "this functionality". I assume what you actually want to do is define your own generic class, which is to say a class that is generic in terms of at least one specific type.

This has been covered in PEP 484 already and discussed multiple times in various configurations on this site. I suggest you read through the PEP section.

I would discourage you from relying on __class_getitem__ for your type annotation purposes as I doubt it will be properly supported by static type checkers any time soon. (see mypy for example). Besides, there is just no need for it, if you can simply subclass typing.Generic.

If you want to do other things inside __class_getitem__, you are of course free to do so, but don't expect static type checkers to even consider your implementation.

In short:

from typing import Generic, TypeVar


T = TypeVar("T")
U = TypeVar("U")


class MyClass(Generic[T, U]):
    pass


Cls = MyClass[int, str]
print(Cls)  # MyClass[int, str]

CodePudding user response:

You were almost there. Use __class_getitem__ instead of __getitem__:

from types import GenericAlias

class MyClass:

    def __init__(self, value: int):
        self.value = value

    def __class_getitem__(cls, klass: type):
        return GenericAlias(cls, klass)

@classmethod is not need if __class_getitem__ is defined on a class: this is the default.

See also the documentation. But take note of the following two paragraphs:

To implement custom generic classes that can be parameterized at runtime and understood by static type-checkers, users should either inherit from a standard library class that already implements __class_getitem__(), or inherit from typing.Generic, which has its own implementation of __class_getitem__().

Custom implementations of __class_getitem__() on classes defined outside of the standard library may not be understood by third-party type-checkers such as mypy. Using __class_getitem__() on any class for purposes other than type hinting is discouraged.

  • Related