Home > Net >  Type hints with map like function?
Type hints with map like function?

Time:05-28

Suppose I've got a map like function:

def generate(data, per_element):
    for element in data:
        per_element(element)

How can I add type-hints so that if I call generate(some_data, some_function) where some_data: List[SomeClass], I get a warning if SomeClass is missing a field used by some_function?

As an example - with the following code:

def some_function(x):
    print(x.value)

some_data: List[int] = [1, 2, 3]

generate(some_data, some_function)

I would like to get a warning that int does not have the attribute value.

CodePudding user response:

Use a type variable to make generate generic in the type of object that data contains and that per_element expects as an argument.

from typing import TypeVar, List, Callable


T = TypeVar('T')


def generate(data: List[T], per_element: Callable[[T], Any]):
    for element in data:
        per_element(element)

class Foo:
    def __init__(self):
        self.value = 3


def foo(x: Foo):
    print(x.value)


def bar(x: int):
    pass


generate([Foo(), Foo()], foo)  # OK

# Argument 2 to "generate" has incompatible type "Callable[[Foo], Any]"; expected "Callable[[int], Any]"
generate([1,2,3], foo)  

Whatever T is, it has to be the same type for both the list and the function, to ensure that per_element can, in fact, be called on every value in data. The error produced by the second call to generate isn't exactly what you asked for, but it essentially catches the same problem: the list establishes what type T is bound to, and the function doesn't accept the correct type.

If you specifically want to require that T be a type whose instances have a value attribute, it's a bit trickier. It's similar to the use case for Protocol, but that only supports methods (or class attributes in general?), not instance attributes, as far as I know. Perhaps someone else can provide a better answer.

CodePudding user response:

Seems like you're searching for:

def generate(data: List[AClass], per_element):
    for element in data:
        per_element(element)

So that AClass implements the method you need.

CodePudding user response:

Your class needs the value attribute:

class SomeClass:
    value: Any  # I used any but use whatever type hint is appropriate

Then using typing.Callable in your function as well as the builtin types. starting with python 3.7 and finally fully implemented in python 3.9 you can use the builtins themselves as well as in python 3.9 you can use parameter specifications

from typing import ParamSpec, TypeVar, Callable

P = ParamSpec("P")
R = TypeVar("R")

def generate(data: list[SomeClass], per_element: Callable[P, R]) -> None:
    for element in data:
        per_element(element)

Then in some_function using the class type hint and None return variable:

def some_function(x: SomeClass) -> None:
    print(x.value)
  • Related