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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With