Another way to implement singleton is to prevent any actual instance creation and use our class itself as a singleton:
class SingletonClass(object): __initialized = False def __new__(cls, *args, **kwargs): if not cls.__initialized: cls.__init__(*args, **kwargs) cls.__initialized = True return cls class MyClass(SingletonClass): @classmethod def __init__(cls, x, y): print "init is here" @classmethod def do(cls): print "doing stuff"
The downside is that we now have to decorate each of our methods with
@classmethod which is inconvenient and defies my original desire that I could turn any existing class into singleton.
OK, so if we need to type
@classmethod string before each and every method we define in
MyClass, why don't we kindly ask Python do it for us? Metaclasses comes to mind - we actually want to convert all methods of the
MyClass to classmethods.
MyClass is created during its type
__new__ stage, so lets hook in:
class SingletonClassMeta(type): def __new__(mcls, *args, **kwargs): cls = super(SingletonClassMeta, mcls).__new__(mcls, *args, **kwargs) for k, v in cls.__dict__.items(): if isinstance(v, types.FunctionType): setattr(cls, k, classmethod(v)) return cls class SingletonClass(object): __metaclass__ = SingletonClassMeta __initialized = False ...
We just check that if
MyClass has an attribute that is plain function, we convert this function to classmethod (decorators are just callables, so we are calling them :). Now the definition of
MyClass is more elegant:
class MyClass(SingletonClass): def __init__(cls, x, y): print "init is here" def do(cls): print "doing stuff"
This is almost perfect. I write my class as usual, just need to inherit from
SingletonClass. I say "almost", because given third-party class
OtherClass I still can not convert it to singleton. The below will NOT make
MyClass.do to behave as if
MyClass was a singleton:
class OtherClass(object): def __init__(cls, x, y): print "init is here" def do(cls): print "doing stuff" class MyClass(SingletonClass, OtherClass): pass
It does not do the trick because the metaclass's
__new__ augments attributes defined in our class (
MyClass that is) and in the later case,
MyClass does not define any attributes at all - they all are in its base class
So how to solve this? Well, we could look through our MRO chain during metacalss
__new__ and to augment attributes of our base classes, but that's quite a destructive process - we screw things up for other regular users of a particular base class.
To overcome this we need to hook in into attribute lookup mechanism of
MyClass. Here is the revised metaclass:
import types class SingletonClassMeta(type): def __getattribute__(cls, attr): val = super(SingletonClassMeta, cls).__getattribute__(attr) if isinstance(val, types.MethodType) and not val.__self__: val = types.MethodType(val.__func__, cls, type(cls)) # the above is equivalent to: # val = val.__func__.__get__(cls, type(cls)) return val
Here our metaclass overloads the attribute lookup machinery:
- We are doing regular attribute lookup
- Then we check that attribute value is an unbound method - by checking that it is a method and its
self is None
- If its an unbound methond, we construct new, bound method on the fly from the original function
There is one rather long but important point to note here:
- Previously, code in
__new__method change attributes of the class before they were ever accessed. Attribute retrieval machinery is described here, but in very short it can be summed as "Look the
__dictof instance, its class and its parents. If whatever you found has
__get__method, call it with current instance and its type and return result to the caller".
- Every python function is a descriptor! That is, when you stuff any function in some class
__dict__and then try to retrieve it later as an attribute, descriptor protocol kicks in and returns bound/unbound methods instead.
- Back to
__new__- when we set class attribute, it needs to be something that would play nice with attribute lookup protocol. We used
classmethodwhich is a decorator object that remembers a function and then acts as descriptor converting remembered function to bound (to class) method when it get fetched. We could (almost) equally just do
setattr(cls, k, functools.partial(v, cls))- it would do the job as well, but would break semantics, because if someone would inspect our class methods it would see
<functools.partial object...instead of
<bound method..., that's why I prefer to use
- In our last example, we took full responsibility for object retrieval. Thus if we try to just return
classmethod(val.__func__)we'd fail with
TypeError: 'classmethod' object is not callableWe took the responsibility, so no one cares if we are returning a descriptor. That is, we need to build and return bound method by ourselves. Again, every function is a descriptor, i.e. every function has
__get__method. You can construct method from any function - it does not even have to belong to any particular class:
>>> class C(object): pass ... >>> def a(): pass ... >>> a.__get__(C(), C) # instance method of C()
> >>> >>> a.__get__(C, type(C)) # class method of C > >>>