Reputation: 7943
I wrote a defaultdict
subclass that can call default_factory
with key as argument.
from collections import defaultdict
from typing import TypeVar, Any, Callable, Generic
K = TypeVar('K')
class keydefaultdict(defaultdict, Generic[K]):
''' Drop-in replacement for defaultdict accepting key as argument '''
default_factory: Callable[[], Any] | Callable[[K], Any] | None
def __missing__(self, key: K) -> Any:
if self.default_factory is None:
raise KeyError(key)
else:
try:
ret = self[key] = self.default_factory(key)
except TypeError: # try no-key signature
ret = self[key] = self.default_factory()
# if failed, let the error propagate as usual
return ret
mypy
complains on the default_factory
type hint:
incompatible type in assignment (expression has type "Callable[[], Any] | Callable[[K], Any] | None", base class "defaultdict" defined the type as "Optional[Callable[[], Any]]")
Is there any way to override the type? mypy
complains also on this lines self.default_factory(...)
- too many arguments (or too few arguments) and in places where this dict is instantiated (incompatible type):
data = keydefaultdict(lambda key: [key])
Argument 1 to "keydefaultdict" has incompatible type "Callable[[Any], list[Any]]"; expected "Optional[Callable[[], Any]]"
Upvotes: 8
Views: 7178
Reputation: 19252
This is intended behavior, as what you're describing violates the Liskov substitution principle. See the mypy documentation for more details.
Using defaultdict
as a subclass is a bad idea for this reason. But, if you really want to get around this (not recommended), you can use # type: ignore[override]
, like so:
default_factory: Callable[[], Any] | Callable[[K], Any] | None # type: ignore[override]
This is described in further detail in the mypy documentation if you want more information.
Upvotes: 7