Skip to main content

Singleton meta class implementation

Project description

Python singleton meta class (singleton-type)

test codecov

One of the problems with a lot of Python singleton implementations is they require you to do their thing. This is a true, and Pythonic, singleton implementation which addresses the problem properly, allowing you to make a class into a singleton without compromise, and without changing the way you instantiate in any way.

The Singleton meta class alters the normal class/object behaviour to make the target class into a singleton, so that __new__ and __init__ will only be called once, and it is thread safe.

Standard use case

The meta class can be used like this:

class OneOfMe(metaclass=Singleton):
    pass

assert id(OneOfMe()) == id(OneOfMe())

If a singleton class is inherited, all sub classes will be singletons (there will be only one of each type), thus:

class AnotherOfMe(OneOfMe, metaclass=Singleton):
    pass

assert id(AnotherOfMe()) == id(AnotherOfMe())
assert id(OneOfMe()) != id(AnotherOfMe())

When creating a singleton in this fashion, the target class's __new__ and __init__ methods will be called only once, as you might expect. Any arguments passed to the constructor will be passed as normal, again as you might expect.

Advanced topics

Most design scenarios where a singleton would not see the possibility that constructor arguments would vary, but if they do, then the second set of arguments will obviously be ineffective. This is almost never a problem because it would simply not be done when a singleton pattern is prescriptive. Some cases may arise where it is desirable to do something more interesting, such as a object/singleton per unique arguments... and this behaviour can be easily implemented by providing some additional methods.

There are three such methods, and your target class must implement all or none of them:

First singleton_ref, this class method is passed the constructor arguments and must return the existing singleton object, or None if not instantiated yet. The implementation need not be thread safe, the over all operation will still be thread safe regardless.

Second singleton_set_ref, this class method is passed the object followed by the constructor arguments, and must SET the singleton object to the object given (such that singleton_ref will return it next time it is called). A lock will be acquired before this class method is called in order to protect thread safety.

Last singleton_detach_ref, this is an object class method rather than a class method, passed no arguments beyond the mandatory self, and it must undo singleton_set_ref such that there is no longer a singleton in effect (there may still be references to the previous singleton object of course). This is an object method rather than a class method so that the detach may depend on state than the class itself.

It should be clear then why all or none of these methods must be implemented by the target class, their operation must work in tandem and of necessity will entirely depend on the differential required behaviour.

The default implementations of these methods will simply get, set and delete an attribute on the class. Here's an example which provides for a different object for different arguments (instead of the default different only for different classes):

class ObjectCache(metaclass=Singleton):
    _cache = {}

    def __init__(self, id):
        self._id = id

    def __new__(cls, id):
        return super().__new__(cls)

    @classmethod
    def singleton_ref(cls, id):
        return cls._cache[id] if id in cls._cache else None

    @classmethod
    def singleton_set_ref(cls, obj, id):
        cls._cache[id] = obj

    def singleton_detach_ref(self):
        del type(self)._cache[self._id]

Here a _cache class attribute is used to retain all instances, one for each unique ID passed to the constructor, and the three singleton methods provide the required implementation for this.

In this case then ObjectCache('foo') will always yield the same object, and ObjectCache('bar') will always yield the same object, but the "foo" and "bar" objects will be different to each other.

The intent here is that the mapping from the parameter domain (class being a parameter of course) to the set of instances can governed in any way desired. Although a less likely requirement, the number of instances can be reduced as well as expanded, so for example the following would cause all the instances of a class and any instances of any sub class, to be a singleton together, so there would only ever be one instance for all the classes:

class SuperClass(metaclass=Singleton):
    _singleton_ref = None

    @classmethod
    def singleton_ref(cls):
        return TestSingletonA._singleton_ref

    @classmethod
    def singleton_set_ref(cls, obj):
        TestSingletonA._singleton_ref = obj

    def singleton_detach_ref(self):
        TestSingletonA._singleton_ref = None

class SubClass(SuperClass):
    pass

assert id(SubClass()) == id(SuperClass())

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

singleton-type-0.0.4.tar.gz (6.8 kB view details)

Uploaded Source

Built Distribution

singleton_type-0.0.4-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file singleton-type-0.0.4.tar.gz.

File metadata

  • Download URL: singleton-type-0.0.4.tar.gz
  • Upload date:
  • Size: 6.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/18.0.1 rfc3986/2.0.0 colorama/0.4.3 CPython/3.8.10

File hashes

Hashes for singleton-type-0.0.4.tar.gz
Algorithm Hash digest
SHA256 b6f60cbfed8e24e270daba170fb771cfd6c99e44f61486d24f0ac1698ebf3380
MD5 3e14a292617032de2ba7ee10b1214217
BLAKE2b-256 d6424b49188253876044a3ae8082d1923b8c998a66a25775bf47fad1e55b48c5

See more details on using hashes here.

File details

Details for the file singleton_type-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: singleton_type-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/18.0.1 rfc3986/2.0.0 colorama/0.4.3 CPython/3.8.10

File hashes

Hashes for singleton_type-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 f482093030dac636891523005388f1c98aae11c3cd84e5dbf3995fdeea8ca9e1
MD5 c8f04a11e63a3cace2e1784ee4579d76
BLAKE2b-256 02f11abf4039d3bf897305c9fbc28d5e72f5a9afb735d3a05549e3051b84cbd8

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page