0

I'm trying to write a Python function that constructs a list with intercepted methods that's reasonably type safe. It intercepts the methods by subclassing the list that's passed.

from typing import Type, TypeVar, List

V = TypeVar("V")
T = TypeVar("T", bound=List[V])


def build_interceptor(list_cls: Type[T]) -> T:
    class LImpl(list_cls):
        def append(self, v: V) -> None:
            print(v)
            super().append(v)

    return LImpl()


l: List[int] = build_interceptor(List[int])
l.append(10)

MyPy isn't happy with this, but the code does work.

main.py:4: error: Type variable "__main__.V" is unbound
main.py:4: note: (Hint: Use "Generic[V]" or "Protocol[V]" base class to bind "V" inside a class)
main.py:4: note: (Hint: Use "V" in function signature to bind "V" inside a function)
main.py:8: error: Variable "list_cls" is not valid as a type
main.py:8: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
main.py:8: error: Invalid base class "list_cls"

I'm not sure what the fixes are. Yes, V is unbound, but I don't really care what it is beyond getting the right return type. I also think there's an issue with making both the list and its contents generic, but I'm not sure how to express that.

3
  • List[int] as a type hint is equivalent to list[int], but it is not itself an actual type. Commented Feb 19, 2022 at 23:33
  • @chepner I've just noticed how opposite that is to more modern languages. In languages with generics built in (Scala, Haskell), list[int] is the type, while list isn't a type but a type constructor. I guess it's not quite opposite, because python's list[int] isn't a type constructor, but hey Commented Feb 19, 2022 at 23:48
  • It's because static typing is being bolted on to a language that is dynamically typed. list[int] at runtime is exactly the same as list; List only exists because it predates the change that allowed the existing object list to be used as a generic. Commented Feb 20, 2022 at 3:25

1 Answer 1

1

I think the problem with 'V' is that it cant be used in the context of a TypeVar, but when defining your new class:

from typing import List, Type, TypeVar

T = TypeVar("T", bound="List")
V = TypeVar("V")

def build_interceptor(list_cls: Type[T]) -> T:
    class LImpl(list_cls[V]):  # type: ignore
        def append(self, v: V) -> None:
            print(v)
            super().append(v)
    return LImpl()

l: List[int] = build_interceptor(List[int])
l.append(10)

This still produces 'Variable "list_cls" is not valid as a type' which is likely related to mypy. It seems to work after adding a type ignore comment.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.