Одноэлементный шаблон Python с подсказками по типам

Я пытался использовать шаблон 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 с правильными подсказками типов.

«Я пытался использовать шаблон Singleton в Python с правильными подсказками типов». Почему?

chepner 20.06.2024 18:34

просто определите функцию, которая возвращает экземпляр, и сделайте класс «частным», назвав его одним подчеркиванием. Вся остальная ерунда с метаклассами совершенно бессмысленна и переусложнена и не дает вам ничего такого, чего не дает описанный выше подход.

juanpa.arrivillaga 20.06.2024 18:35

@chepner в первую очередь кэширует. Мой класс обслуживания требует больших затрат на инициализацию. Поэтому я бы избегал необходимости создавать его экземпляры везде, где это необходимо. Я не знал, будет ли для этого хорошей идеей иметь глобальную переменную.

Vikash Balasubramanian 20.06.2024 19:35

Для синглтона не может быть указан тип, в том смысле, что подсказки типа не дают возможности использовать такие механизмы, как сравнения is, для сужения типов (например, вы не можете реплицировать None для другой конструкции Python). В вашей попытке есть несколько проблем, но это не имеет ничего общего с синглтонами.

dROOOze 20.06.2024 20:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
104
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот наиболее разумный способ определить шаблон «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 20.06.2024 19:59

@STerliakov правда, это абсолютно верно

juanpa.arrivillaga 20.06.2024 20:21

Или даже просто Foo = functools.cache(_Foo).

chepner 20.06.2024 21:26

Другие вопросы по теме