Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to type hint an instance-level function (i.e. not a method)?

Instance attributes are typically annotated on the class:

class Foo:
    x: int

    def __init__(self, x):
        self.x = x

Foo(0).x

This works and mypy doesn't report any issues. However, when the instance attribute is a Callable then mypy starts complaining:

from typing import Callable


class Foo:
    func: Callable[[], int]

    def __init__(self, func):
        self.func = func


Foo(lambda: 0).func()

I get the following error:

test.py:11: error: Attribute function "func" with type "Callable[[], int]" does not accept self argument
Found 1 error in 1 file (checked 1 source file)

Since this function is not defined on the class, but only stored in the instance dict, it won't be bound to the instance during attribute lookup (in short: the above snippet works). So I don't see why mypy would complain about this. Is there another way to type hint such instance-level functions?

like image 514
a_guest Avatar asked Jun 05 '26 07:06

a_guest


1 Answers

This is currently broken in mypy as it assumes you are creating a method, here is the relevant issue https://github.com/python/mypy/issues/708.

Typing the function in the init works fine as it won't think it's a method on the class, the following code passes type checking properly and func's type is inferred from the parameter. The attribute assignment can also be typed directly if the parameter is not viable.

from collections.abc import Callable

class Foo:
    def __init__(self, func: Callable[[], int]):
        self.func = func

reveal_type(Foo(lambda: 0).func)
###OUTPUT###
file.py:7: note: Revealed type is "def () -> builtins.int"

An another workaround that can be found in the issue and avoids assigning in the init is to use a callback Protocol like so:

from typing import Protocol

class FuncCallback(Protocol):
    def __call__(self, /) -> int:
        ...

class Foo:
    func: FuncCallback

    def __init__(self, func):
        self.func = func

This makes func a FuncCallback protocol which expects no arguments when called and returns an int like your Callable.

like image 137
Numerlor Avatar answered Jun 07 '26 22:06

Numerlor