如何正确 override/overload 输入 __call__
How to correctly override/overload typing of __call__
我需要说明类型,统一工厂返回。我在单独的文件中有单例工厂,如下所示:
from typing import Any, Type
class SingletonFactory:
__slots__ = ('singleton_instance', )
def __init__(self, singleton_class: Type[object], **singleton_init_params: Any):
self.singleton_instance: object = singleton_class(**singleton_init_params) # type: ignore[call-arg] # noqa: E501
def __call__(self) -> object:
return self.singleton_instance
然后在其他文件中。
选项 1:
from typing import Callable, overload
class Client:
pass # IRL have init params
client_factory: Callable[[], Client] = SingletonFactory(
Client
)
client = client_factory() # pyCharm see it as instance of object - expected Client
MyPy 错误:
error: Incompatible types in assignment (expression has type "SingletonFactory", variable has type "Callable[[], Client]") [assignment]
note: "SingletonFactory.__call__" has type "Callable[[], object]"
选项 2:
from typing import Callable, overload
class Client:
pass # IRL have init params
@overload # type: ignore[misc]
def client_factory() -> Client:
...
client_factory = SingletonFactory(
Client
)
client = client_factory() # pyCharm see it as instance of Client - what I expect
有效,但 MyPy 错误与上面相同加上:error: Single overload definition, multiple required [misc]
.
选项 3:
from typing import Callable, overload
class Client:
pass # IRL have init params
class ClientFactory(SingletonFactory):
@overload # type: ignore[misc]
def __call__() -> Client:
...
client_factory = ClientFactory(
Client
)
client = client_factory() # pyCharm see it as instance of Client - what I expect
少了一个MyPy错误,但是继承SingletonFactory
只是为了重载使得代码很繁琐。
有没有办法完全满足这种情况下的MyPy?
您正在寻找 generics,以描述代码中不同变量类型之间的关系;下面的代码演示了一种这样的实现。
from typing import Callable, Generic, ParamSpec, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
class SingletonFactory(Generic[T]):
__slots__ = ('singleton_instance', )
singleton_instance: T
def __init__(
self,
singleton_class: Callable[P, T],
*singleton_args: P.args,
**singleton_kwargs: P.kwargs
):
self.singleton_instance = singleton_class(*singleton_args, **singleton_kwargs)
def __call__(self) -> T:
return self.singleton_instance
现在 __call__
的 return 类型基于 __init__
的第一个参数的类型:
class Thing: pass
t: Thing = SingletonFactory(Thing)() # OK
或者,您可以通过提供通用类型在某处显式指定特定类型的工厂:
def my_func(factory: SingletonFactory[int]) -> int:
return factory()
应该注意的是,不是使用 Type[T]
,而是使用 Callable[P, T]
和 ParamSpec
(在 Python 3.10 中实现)。当程序是这样写的时候,它也允许检查参数,基于 singleton_class
:
class OtherThing:
def __init__(self, foo: int, bar: str):
self.foo = foo
self.bar = bar
ot: OtherThing = SingletonFactory(OtherThing)() # error: Missing positional arguments "foo", "bar" in call to "SingletonFactory"
ot = SingletonFactory(OtherThing, 123, bar="baz")() # OK
...您不再需要 type: ignore[call-arg]
.
我需要说明类型,统一工厂返回。我在单独的文件中有单例工厂,如下所示:
from typing import Any, Type
class SingletonFactory:
__slots__ = ('singleton_instance', )
def __init__(self, singleton_class: Type[object], **singleton_init_params: Any):
self.singleton_instance: object = singleton_class(**singleton_init_params) # type: ignore[call-arg] # noqa: E501
def __call__(self) -> object:
return self.singleton_instance
然后在其他文件中。
选项 1:
from typing import Callable, overload
class Client:
pass # IRL have init params
client_factory: Callable[[], Client] = SingletonFactory(
Client
)
client = client_factory() # pyCharm see it as instance of object - expected Client
MyPy 错误:
error: Incompatible types in assignment (expression has type "SingletonFactory", variable has type "Callable[[], Client]") [assignment]
note: "SingletonFactory.__call__" has type "Callable[[], object]"
选项 2:
from typing import Callable, overload
class Client:
pass # IRL have init params
@overload # type: ignore[misc]
def client_factory() -> Client:
...
client_factory = SingletonFactory(
Client
)
client = client_factory() # pyCharm see it as instance of Client - what I expect
有效,但 MyPy 错误与上面相同加上:error: Single overload definition, multiple required [misc]
.
选项 3:
from typing import Callable, overload
class Client:
pass # IRL have init params
class ClientFactory(SingletonFactory):
@overload # type: ignore[misc]
def __call__() -> Client:
...
client_factory = ClientFactory(
Client
)
client = client_factory() # pyCharm see it as instance of Client - what I expect
少了一个MyPy错误,但是继承SingletonFactory
只是为了重载使得代码很繁琐。
有没有办法完全满足这种情况下的MyPy?
您正在寻找 generics,以描述代码中不同变量类型之间的关系;下面的代码演示了一种这样的实现。
from typing import Callable, Generic, ParamSpec, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
class SingletonFactory(Generic[T]):
__slots__ = ('singleton_instance', )
singleton_instance: T
def __init__(
self,
singleton_class: Callable[P, T],
*singleton_args: P.args,
**singleton_kwargs: P.kwargs
):
self.singleton_instance = singleton_class(*singleton_args, **singleton_kwargs)
def __call__(self) -> T:
return self.singleton_instance
现在 __call__
的 return 类型基于 __init__
的第一个参数的类型:
class Thing: pass
t: Thing = SingletonFactory(Thing)() # OK
或者,您可以通过提供通用类型在某处显式指定特定类型的工厂:
def my_func(factory: SingletonFactory[int]) -> int:
return factory()
应该注意的是,不是使用 Type[T]
,而是使用 Callable[P, T]
和 ParamSpec
(在 Python 3.10 中实现)。当程序是这样写的时候,它也允许检查参数,基于 singleton_class
:
class OtherThing:
def __init__(self, foo: int, bar: str):
self.foo = foo
self.bar = bar
ot: OtherThing = SingletonFactory(OtherThing)() # error: Missing positional arguments "foo", "bar" in call to "SingletonFactory"
ot = SingletonFactory(OtherThing, 123, bar="baz")() # OK
...您不再需要 type: ignore[call-arg]
.