I saw a lot of methods of making a singleton in Python and I tried to use the metaclass implementation with Python 3.2 (Windows), but it doesn"t seem to return the same instance of my singleton class.
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class MyClass(object):
__metaclass__ = Singleton
a = MyClass()
b = MyClass()
print(a is b) # False
I use the decorator implementation now which is working, but I'm wondering what is wrong with this implementation?
The metaclass syntax has changed in Python3. See the documentaition.
class MyClass(metaclass=Singleton):
pass
And it works:
>>> MyClass() is MyClass()
True
Related
I am trying to create a Singleton class in Python using this code:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def clear(cls):
cls._instances = {}
class MyClass(metaclass=Singleton):
def my_attribute(*args):
if len(args) == 1:
MyClass.i_attribute = args[0]
elif len(args) == 0:
try:
return MyClass.i_attribute
except:
MyClass.i_attribute = 0
but the clear() method does not seem to work:
MyClass.my_attribute(42)
MyClass.clear()
MyClass.my_attribute() # still returns 42, but I expect 0
How do I delete the instance of MyClass so that I am back to 0 instances?
The singleton metaclass collects in the _instances attribute all the instantiated children. Therefore, if you want to clear the _instances attributes only for a specific class, you can:
Redefine the Singleton class, to make _instances an attribute of the class instantiated by the metaclass:
class Singleton(type):
"""
Singleton metaclass, which stores a single instance of the children class in the children class itself.
The metaclass exposes also a clearing mechanism, that clear the single instance:
* clear: use as follows 'ClassToClear.clear()
"""
def __init__(cls, name, bases, methods):
cls._instance = None
super().__init__(name, bases, methods)
def __call__(cls, *args, **kwargs):
if cls._instance:
return cls._instance
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
def clear(cls):
cls._instance = None
Using this new definition of the singleton, you can write a clear method that can be called by any of the classes initialized with the Singleton metaclass and it will clear the _instance attribute.
So in your case, MyClass.clear() would reset the _instance attribute to None.
Add a clear method, which removes only the children class from the Singleton._instances dictionary:
class SingletonRegistry(type):
"""
Singleton metaclass, which implements a registry of all classes that are created through this metaclass and
the corresponding instance of that class (added at the first creation).
The metaclass exposes also a clearing mechanism, that clears a specific class from the registry:
* clear: use as follows 'ClassToClear.clear()
* clear_all: use as follows 'SingletonRegistry.clear_all()
"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(SingletonRegistry, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def clear(cls):
_ = cls._instances.pop(cls, None)
def clear_all(*args, **kwargs):
SingletonRegistry._instances = {}
In this case, if you would like to clear only one specific child class, then you could write MyClass.clear(), which will cause the MyClass key to be removed from Singleton._instances dictionary.
This structure allows also to clear all key in the _instances dictionary by writing SingletonRegistry.clear_all().
user2357112 is right. Here is the correct code:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def clear(cls):
cls._instances = {}
class MyClass(metaclass=Singleton):
def my_attribute(*args):
my = MyClass()
if len(args) == 1:
my.i_attribute = args[0]
elif len(args) == 0:
try:
return my.i_attribute
except:
my.i_attribute = 0
return my.i_attribute
Below is the well known code for creating a singleton metaclass:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
cls.x = 5
return cls._instances[cls]
class MyClass(metaclass=Singleton):
pass
m = MyClass()
v = MyClass()
print (m.x)
m.x = 420
print (v.x)
My question is why do we need to use the call function of type class again to initialize the class? Why can't we call the init method to do that like normal class initialization. Something like this :
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = cls(*args, **kwargs)
cls.x = 5
return cls._instances[cls]
class MyClass(metaclass=Singleton):
pass
m = MyClass()
v = MyClass()
print (m.x)
m.x = 420
print (v.x)
This is getting into an infinite loop anyways.
Because trying to creating an instance of a class by just calling it as you do in the line cls._instances[cls] = cls(*args, **kwargs) just by itself calls the metaclass __call__ which is the exact method where you attempt the call, as is explained here.
Now, if one thing, you should not really be using metaclasses just for creating singletons.
The Metaclass mechanism in Python is complicated - the problem you've hit on this question shows you are grasping now how simple inheritance and call to methods on super-classes work - and metaclasses are an order of magnitude more complicated than that.
And, beyond been complicated, classes with a custom metaclass can't be ordinarily combined with other classes that feature custom metaclasses, so the general rule is keeping their usage to a minimum anyway.
How to create a singleton class:
But for creatign a singleton, you can just place all your checks in the class ordinary __new__ method. No need to feedle with metaclasses - just plain class inheritance:
_instances = {}
class Singleton(object):
def __new__(cls, *args, **kw):
if not cls in _instances:
instance = super().__new__(cls)
_instances[cls] = instance
return _instances[cls]
And just inherit your singleton classes from this one.
As answered brilliantly by agf here, a possible implementation of a Singleton in Python could be using this metaclass:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
However, I do not understand the use of _instance. I know it's a dict used to store instances indexed by their class. However, it is declared as a class attribute (_instances = {}), and it is used as an attribute of cls (if cls not in cls._instances:). How can the two be the same?
I'm writing a metaclass, and I want an additional method to be called between __new__ and __init__.
If I were calling the method before __new__ or after __init__ I could write e.g.
class Meta(type):
def __call__(cls):
ret = type.__call__()
ret.extraMethod()
My temptation is to write
class Meta(type):
def __call__(cls):
ret = cls.__new__(cls)
ret.extraMethod()
ret.__init__()
return ret
and just reproduce the functionality of type.__call__ myself. But I'm afraid there might be some subtlety to type.__call__ I have omitted, which will lead to unexpected behavior when my metaclass is implemented.
I cannot call extraMethod from __init__ or __new__ because I want users of my metaclass to be able to override __init__ and __new__ as in normal Python classes, but to still execute important set-up code in extraMethod.
Thanks!
If you really wish to do exactly what you said I can suggest you the following solution:
def call_after(callback, is_method=False):
def _decorator(func):
def _func(*args, **kwargs):
result = func(*args, **kwargs)
callback_args = (result, ) if is_method else ()
callback(*callback_args)
return result
return _func
return _decorator
class Meta(type):
def __new__(mcs, class_name, mro, attributes):
new_class = super().__new__(mcs, class_name, mro, attributes)
new_class.__new__ = call_after(
new_class.custom_method,
is_method=True
)(new_class.__new__)
return new_class
class Example(object, metaclass=Meta):
def __new__(cls, *args, **kwargs):
print('new')
return super().__new__(cls, *args, **kwargs)
def __init__(self):
print('init')
def custom_method(self):
print('custom_method')
if __name__ == '__main__':
Example()
This code will generate the following result:
new
custom_method
init
I have a class which, by design, must follow the singleton pattern. So I went ahead and implemented it using a metaclass. Everything worked nicely until a bug was reported which, in summary, said that deepcopy-ied instances of my singleton class were not the same instances.
I can get around this bug by inheriting from a base singleton-type class, but I'd rather not, for reasons pointed out in this question.
A working example of this issue is presented below:
class SingletonMeta(type):
def __init__(cls, name, bases, dict):
super(SingletonMeta, cls).__init__(name, bases, dict)
cls.instance = None
def __call__(cls,*args,**kw):
print "SingletonMeta __call__ was called"
if cls.instance is None:
cls.instance = super(SingletonMeta, cls).__call__(*args, **kw)
return cls.instance
class MyClass1(object):
__metaclass__ = SingletonMeta
class SingletonBase(object):
_instance = None
def __new__(class_, *args, **kwargs):
print "SingletonBase __new__ was called"
if not isinstance(class_._instance, class_):
class_._instance = object.__new__(class_, *args, **kwargs)
return class_._instance
class MyClass2(SingletonBase):
pass
from copy import deepcopy as dcp
mm1 = MyClass1()
mm2 = dcp(mm1)
print "mm1 is mm2:", mm1 is mm2
mb1 = MyClass2()
mb2 = dcp(mb1)
print "mb1 is mb2:", mb1 is mb2
Output:
SingletonMeta __call__ was called
mm1 is mm2: False
SingletonBase __new__ was called
SingletonBase __new__ was called
mb1 is mb2: True
Can you give me any pointers as to how should one resolve this issue? I'm running Python 2.7.X
The docs on the copy module say this:
In order for a class to define its own copy implementation, it can define special methods __copy__() and __deepcopy__().
[...]
The latter is called to implement the deep copy operation; it is passed one argument, the memo dictionary.
[...]
So if you declare these to return self, that ought to do the trick.
When you need to customize class creation (not instance creation), you do it in the __new__ method of the metaclass:
def __new__(cls, name, bases, dict):
dict['__deepcopy__'] = dict['__copy__'] = lambda self, *args: self
return super(SingletonMeta, cls).__new__(cls, name, bases, dict)
and your test will give
SingletonMeta __call__ was called
mm1 is mm2: True
You need to define __copy__ as well or even shallow copies will result in new instances.
Glad that my solution in that thread came in handy.