Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Python typing for a metaclass Singleton

I have a Python (3.8) metaclass for a singleton as seen here

I’ve tried to add typings like so:

from typing import Dict, Any, TypeVar, Type

_T = TypeVar("_T", bound="Singleton")


class Singleton(type):
    _instances: Dict[Any, _T] = {}

    def __call__(cls: Type[_T], *args: Any, **kwargs: Any) -> _T:
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

In the line:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

_instances: Dict[Any, _T] = {}

MyPy warns:

Mypy: Type variable "utils.singleton._T" is unbound

I’ve tried different iterations of this to no avail; it’s very hard for me to figure out how to type this dict.

Further, the line:

def __call__(cls: Type[_T], *args: Any, **kwargs: Any) -> _T:

Produces:

Mypy: The erased type of self "Type[golf_ml.utils.singleton.Singleton]" is not a supertype of its class "golf_ml.utils.singleton.Singleton"

How could I correctly type this?

>Solution :

This should work:

from __future__ import annotations

import typing as t


_T = t.TypeVar("_T")


class Singleton(type, t.Generic[_T]):

    _instances: dict[Singleton[_T], _T] = {}

    def __call__(cls, *args: t.Any, **kwargs: t.Any) -> _T:
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

Rough explanations:

  1. _T = TypeVar("_T", bound="Singleton") is not correct – Singleton is type(type(obj)) where obj: _T = Singleton.__call__(...). In proper usage, the argument to bound= can only be type(obj) or some union typing construct, not type(type(obj).
  2. Type variable "_T" is unbound indicates that you need to make Singleton generic with respect to _T to bind _T.
  3. The erased type of self ... error message is telling you that you’re committing an error that will go away once you fix (1).

Runtime is important too, so the final sanity check is to make sure you’ve actually implemented a singleton using this metaclass:

class Logger(metaclass=Singleton):
    pass

>>> print(Logger() is Logger())
True
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading