鉴于mypy的当前限制,此装饰器输入正确吗?我在下面提供了示例用法:
import functools
from typing import TypeVar, Type, Any, cast
C = TypeVar('C', bound=Type[Any])
def singleton(cls: C) -> C:
"""Transforms a class into a Singleton (only one instance can exist)."""
@functools.wraps(cls)
def wrapper(*args: Any, **kwargs: Any) -> Any:
if not wrapper.instance: # type: ignore # https://github.com/python/mypy/issues/2087
wrapper.instance = cls(*args, **kwargs) # type: ignore # https://github.com/python/mypy/issues/2087
return wrapper.instance # type: ignore # https://github.com/python/mypy/issues/2087
wrapper.instance = None # type: ignore # https://github.com/python/mypy/issues/2087
return cast(C, wrapper)
@singleton
class Test:
pass
if __name__ == '__main__':
a = Test()
b = Test()
print(a is b)
我必须在显示type: ignore
属性的行上添加instance
,因为否则mypy会标记以下错误:
error: "Callable[..., Any]" has no attribute "instance"
答案 0 :(得分:1)
您的函数采用C
类型的参数,并返回相同类型的结果。因此,根据mypy a
和b
将具有相同的类型。您可以使用reveal_type
进行检查。
reveal_type(a) # Revealed type is 'test.Test'
reveal_type(b) # Revealed type is 'test.Test'
无论如何,cast
和# type: ignore
都应谨慎使用,因为它们告诉mypy信任您(开发人员)类型正确,即使它无法确认。
我在您的代码中看到的潜在问题是您正在用一个函数替换一个类(即Test
),这可能会破坏一些代码。例如:
>>> Test
<function Test at 0x7f257dd2bae8>
>>> Test.mro()
AttributeError: 'function' object has no attribute 'mro'
您可以尝试的另一种方法是替换装饰类的__new__
方法:
def singleton(cls: C) -> C:
"""Transforms a class into a Singleton (only one instance can exist)."""
new = cls.__new__
def _singleton_new(cls, *args, **kwds):
try:
inst = cls._instance
except AttributeError:
cls._instance = inst = new(cls, *args, **kwds)
return inst
cls.__new__ = _singleton_new
return cls
在这种情况下,您不会替换整个类,因此使用该类破坏其他代码的可能性较小:
>>> Test
test.Test
>>> Test.mro()
[test.Test, object]
请注意,以上代码仅是示例,显示了您当前方法的局限性。因此,您可能不应该按原样使用它,而是寻找更可靠的解决方案。