Package: python-singleton-metaclasses
Python Singleton MetaclassesDescriptionA package of python metclasses for implementing singleton and related patterns.Singleton ClassThis implements the traditional singleton pattern. Once an instance of a class is created, all future instantiations result in the same object.Memoized ClassThis applies the notion of "memoization" to object creation. Multiple calls to a constructor with the same first parameter result in the same object.Calls to the constructor with a new first parameter result in a new object instance.This is useful in situations where a singleton is called for if the arguments are the same, but a new instance is called for when new arguments are provided.For example, if an object parses a file, there may be no need to parse the same file more than once. So all instantiations of the parser on the same file result in the same object instance. But for a new file that has yet to be parsed, a new instance will be created.Alowing Object CleanupOne drawback is the singleton (and memoized) object does not get freed. If it is desired for the singleton objects to be destroyed no longer required, there are a couple of options:In the class definition, declare a class variable,_PYSINGLETON_WEAKREF = True(or_PYMEMOIZED_WEAKREFas appropriate)Create an environment variable constructed from the class name and set it to '1':export MySingleton_WEAKREF=1This causes instances to be tracked by the metaclass using weak references. The garbage collector will destroy them when the last normal reference has gone out of scope. The next time the class is instantiated, a new instance will be created.ExamplesSingleton Class ExamplefrompysingletonimportPySingleton# noqa: E402classMySingleton(metaclass=PySingleton):def__init__(self,value):self.value=value# Create first instance and inspect valuemy_singleton_1=MySingleton(1)print("my_singleton_1.value:{}".format(my_singleton_1.value))# Create second instance with new constructor parammy_singleton_2=MySingleton(2)# Inspect value and see it matches the originalprint("my_singleton_2.value:{}".format(my_singleton_2.value))# First and second instances' values are equalprint("my_singleton_1.value == my_singleton_2.value:{}".format(my_singleton_1.value==my_singleton_2.value))# change second instance's valuemy_singleton_2.value=7# Inspect first instance's value and see it has changedprint("my_singleton_1.value:{}".format(my_singleton_1.value))# First and second instance have the same object ID:print("id(my_singleton_1):{:#x}".format(id(my_singleton_1)))print("id(my_singleton_2):{:#x}".format(id(my_singleton_2)))$python./example.pymy_singleton_1.value: 1my_singleton_2.value: 1my_singleton_1.value == my_singleton_2.value: Truemy_singleton_1.value: 7id(my_singleton_1): 0x101c29b80id(my_singleton_2): 0x101c29b80Memoized Class ExamplefrompysingletonimportPyMemoized# noqa: E402classMyMemoized(metaclass=PyMemoized):def__init__(self,param1,param2):self.value=param2# Create first instance and inspect valuemy_memoized_1=MyMemoized("arg1",1)print("my_memoized_1.value:{}".format(my_memoized_1.value))# Create second instance with the original param1 but new param2my_memoized_2=MyMemoized("arg1",2)# Inspect value and see it matches the originalprint("my_memoized_2.value:{}".format(my_memoized_2.value))# First and second instances' values are equalprint("my_memoized_1.value == my_memoized_2.value:{}".format(my_memoized_1.value==my_memoized_2.value))# change second instance's valuemy_memoized_2.value=7# Inspect first instance's value and see it has changedprint("my_memoized_1.value:{}".format(my_memoized_1.value))# Create third instance with new param1 & param2, and see# it takes the new valuemy_memoized_3=MyMemoized("arg3",3)print("my_memoized_3.value:{}".format(my_memoized_3.value))# First and third  instances' values are not equalprint("my_memoized_1.value == my_memoized_3.value:{}".format(my_memoized_1.value==my_memoized_3.value))# First and second instance have the same object IDprint("id(my_memoized_1):{:#x}".format(id(my_memoized_1)))print("id(my_memoized_2):{:#x}".format(id(my_memoized_2)))# but third instance's object ID is differentprint("id(my_memoized_3):{:#x}".format(id(my_memoized_3)))python3 ./examples/memoized-example.pymy_memoized_1.value: 1my_memoized_2.value: 1my_memoized_1.value == my_memoized_2.value: Truemy_memoized_1.value: 7my_memoized_3.value: 3my_memoized_1.value == my_memoized_3.value: Falseid(my_memoized_1): 0x100773b80id(my_memoized_2): 0x100773b80id(my_memoized_3): 0x100818340