Я пытался использовать шаблон Singleton в Python с правильными подсказками типов.
Вот моя попытка
from typing import Any, TypeVar, override
T = TypeVar('T', bound='SingletonBase')
class Singleton(type):
_instances: dict[type[T], T] = {} # type variable T has no meaning in this context
@override
def __call__(cls: type[T], *args: Any, **kwargs: dict[str, Any]) -> T:
if cls not in cls._instances: # Access to generic instance variable through class is ambiguous
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance # Access to generic instance variable through class is ambiguous
return cls._instances[cls] # Expression of type T is incompatible with return type T@__call__
class SingletonBase(metaclass=Singleton):
pass
Я получаю много жалоб от проверки типов. (Я аннотирую свой код жалобами в виде комментариев)
Я нашел этот ответ Подсказка типа для общего синглтона?
Но это обрабатывает только аннотации для возвращаемого типа. Я хотел бы понять, что здесь происходит, и узнать, как реализовать шаблон Singleton с правильными подсказками типов.
просто определите функцию, которая возвращает экземпляр, и сделайте класс «частным», назвав его одним подчеркиванием. Вся остальная ерунда с метаклассами совершенно бессмысленна и переусложнена и не дает вам ничего такого, чего не дает описанный выше подход.
@chepner в первую очередь кэширует. Мой класс обслуживания требует больших затрат на инициализацию. Поэтому я бы избегал необходимости создавать его экземпляры везде, где это необходимо. Я не знал, будет ли для этого хорошей идеей иметь глобальную переменную.
Для синглтона не может быть указан тип, в том смысле, что подсказки типа не дают возможности использовать такие механизмы, как сравнения is
, для сужения типов (например, вы не можете реплицировать None
для другой конструкции Python). В вашей попытке есть несколько проблем, но это не имеет ничего общего с синглтонами.
Вот наиболее разумный способ определить шаблон «singleton» в Python:
class _Foo:
_instance: "_Foo" = None
def Foo() -> _Foo:
if _Foo._instance is None:
_Foo._instance = _Foo()
return _Foo._instance
Вышеупомянутое хорошо сочетается с аннотациями типов, не требует метакласса и дает вам точно такую же гарантию, что кто-то не создаст несколько экземпляров вашего класса (что не является гарантией, поскольку даже с вашим примером метакласса кто-то может определить несколько экземпляров вашего класса).
Или, как указано в комментариях, еще проще:
import functools
class _Foo:
pass
@functools.cache
def Foo() -> _Foo:
return _Foo()
Даже эти махинации с частными переменными класса не нужны, если вам не нужны аргументы: просто functools.cache
это фабрика!
@STerliakov правда, это абсолютно верно
Или даже просто Foo = functools.cache(_Foo)
.
«Я пытался использовать шаблон Singleton в Python с правильными подсказками типов». Почему?