1

Is there an inverse function for Type[SomeType] so that Instance[Type[SomeType]] == SomeType?

I'm given a class and I'd like to annotate the return value of calling its constructor

class FixedSizeUInt(int):
    size: int = 0
    def __new__(cls, value: int):
        cls_max: int = cls.max_value()
        if not 0 <= value <= cls_max:
            raise ValueError(f"{value} is outside range " +
                             f"[0, {cls_max}]")
        new: Callable[[cls, int], Instance[cls]] = super().__new__  ### HERE
        return new(cls, value)

    @classmethod
    def max_value(cls) -> int:
        return 2**(cls.size) - 1

Edit: This class is abstract, it needs to be subclassed for it to make sense, as a size of 0 only allows for 0 as its value.

class NodeID(FixedSizeUInt):
    size: int = 40

class NetworkID(FixedSizeUInt):
    size: int = 64

Edit 2: For this specific case, using generics will suffice, as explained in https://stackoverflow.com/a/39205612/5538719 . Still, the question of a inverse of Type remains. Maybe the question then is: Will generics cover every case so that an inverse function is never needed?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Facundo
  • 131
  • 1
  • 8
  • 1
    Can you please clarify what you want to annotate here? The return value of ``__new__`` is just ``FixedSizeUInt``. Do you want to parameterize the type of ``cls``, so that it works for all subclasses? – MisterMiyagi Jun 24 '20 at 14:56
  • Does this answer your question? [Can you annotate return type when value is instance of cls?](https://stackoverflow.com/questions/39205527/can-you-annotate-return-type-when-value-is-instance-of-cls) – MisterMiyagi Jun 24 '20 at 14:58
  • @MisterMiyagi Yes, exactly. I need it to be generic so it'll work for subclasses, as you can see the size variable is set to zero, so it would not make sense to use this class without subclassing and changing it to something other than zero – Facundo Jun 24 '20 at 15:06
  • 1
    Can you clarify what you mean by the edit? Your question will be re-opened if it is sufficiently different from the other question. ``T`` literally means "instance of type ``T``", so given ``cls: Type[T]`` a ``T`` is also "a ``Instance[Type[T]]``" (if such a thing were defined). – MisterMiyagi Jun 24 '20 at 15:17
  • Is that enough to cover all cases such that ``Instance[Type[T]]`` would not be needed? – Facundo Jun 24 '20 at 15:22
  • I've added something about that to my answer… – deceze Jun 24 '20 at 15:25

1 Answers1

1

I believe you want:

new: Callable[[Type[FixedSizeUInt], int], FixedSizeUInt] = ...

Or a little more dynamically:

from typing import TypeVar, Callable

T = TypeVar('T')

...

def __new__(cls: Type[T], value: int):
    ...
    new: Callable[[Type[T], int], T] = ...

Still, the question of a inverse of Type remains. Maybe the question then is: Will generics cover every case so that an inverse function is never needed?

It's not about generics, it's about type hints in general. Take int as an example. int is the class. int() creates an instance of the class. In type hints, int means instance of int. Using a class as a type hint always talks about an instance of that type, not the class itself. Because talking about instances-of is the more typical case, talking about the class itself is less common.

So, you need to use a class in a type hint and a class in a type hint means instance of that class. Logically, there's no need for an Instance[int] type hint, since you cannot have a non-instance type hint to begin with. On the contrary, a special type hint Type[int] is needed for the special case that you want to talk about the class.

deceze
  • 510,633
  • 85
  • 743
  • 889
  • Will the first option work for subclasses? – Facundo Jun 24 '20 at 15:07
  • No, that's why you'd use a `TypeVar` instead. – deceze Jun 24 '20 at 15:15
  • Just to clarify, I agree that ``Instance[int]`` makes no sense, I was talking about ``Instance[cls]`` where ``cls: Type[int]`` so that you would not need to set up a type variable for a simple case like the one presented in the example. – Facundo Jun 24 '20 at 15:28
  • `cls` isn't a *static* type hint to begin with, it cannot be evaluated by static type checkers. Only at runtime does `cls` receive a value (which happens to be a class) and only then could it be evaluated. That's why you need `cls: `, to be able to talk about "whatever *type* `cls` has". – deceze Jun 24 '20 at 15:37