David Ehrmann
David Ehrmann

Reputation: 7576

Generic function parameters in Python

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.

Upvotes: 0

Views: 2500

Answers (1)

Deric
Deric

Reputation: 180

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.

Upvotes: 1

Related Questions