Typing: Forward-comapbility of my "Parametrized-generic" class

Created on 22 Jan 2019  路  5Comments  路  Source: python/typing

Sorry for questioning here... If there is a better place will be grateful for pointing to :)

I'm looking for code-saving algorithm of construction generics. And, explored typing module a bit, came to the solution, workes for me:

import typing as ty
T = ty.TypeVar('T')
class A(ty.Generic[T]):
    # __args are unique every instantiation
    __args: ty.Optional[ty.Tuple[ty.Type[T]]] = None
    value: T

    def __init__(self, value: ty.Optional[T]=None) -> None:
        """Get actual type of generic and initizalize it's value."""
        cls = ty.cast(A, self.__class__)
        if cls.__args:
            self.ref = cls.__args[0]
        else:
            self.ref = type(value)
        if value:
            self.value = value
        else:
            self.value = self.ref()
        cls.__args = None

    def __class_getitem__(cls, *args: type) -> ty.Type['A']:
        """Recive type args, if passed any before initialization."""
        cls.__args = ty.cast(ty.Tuple[ty.Type[T]], args)
        return super().__class_getitem__(*args, **kwargs)  # type: ignore

a = A[int]()
b = A(int())
c = A[str]()
print([a.value, b.value, c.value])  # [0, 0, '']

How dangerous to use this internal interpretation of typing public API?

question

All 5 comments

What exactly do you want to achieve? Do you want to access type argument of the original class on an instance? For this you can use obj.__orig_class__.__args__:

>>> class A(Generic[T]): ...
... 
>>> A[int]().__orig_class__.__args__
(<class 'int'>,)

How dangerous to use this internal interpretation of typing public API?

Exactly the same amount of danger as while using other private Python APIs -- they can change without notice at any moment.

Also _questions_ (not bug reports or feature requests) are better discussed in the typing Gitter channel.

But obj.__orig_class__ is appeared after object construction and initialization, and after all it's bases and metaclasses initialization)

What exactly do you want to achieve?

real-time duck-typing for users didn't bother by static one))
P.S. Also, it can be useful for some fabrics

>>> class A(Generic[T]):
...     def __init__(self):
...         self.value = self.__orig_class__.__args__[0]()
... 
>>> 
>>> A[int]().value
0

Anyway, you can use whatever way. Maybe __class_getitem__ is even slightly better, at least __class_getitem__ is a documented special method (although its behavior for generics is not).

ehhm...

Traceback (most recent call last):
  File "E:\packages\pyksp\pyksp\simple_test.py", line 64, in <module>
    A[int]().value
  File "C:\Users\Levitanus\AppData\Local\Programs\Python\Python37-32\lib\typing.py", line 670, in __call__
    result = self.__origin__(*args, **kwargs)
  File "E:\packages\pyksp\pyksp\simple_test.py", line 61, in __init__
    self.value = self.__orig_class__.__args__[0]()
AttributeError: 'A' object has no attribute '__orig_class__'

You see, this is how dangerous using internal APIs (also your example crashes as well in Python 3.6 and older).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mitar picture mitar  路  5Comments

saulshanabrook picture saulshanabrook  路  6Comments

gvanrossum picture gvanrossum  路  8Comments

JelleZijlstra picture JelleZijlstra  路  6Comments

Phlogistique picture Phlogistique  路  4Comments