@staticmethod with @property
User kaizer.se was onto something as far as the original question goes. I took it a step further in terms of simplicity, so that it now requires only a single decorator:
class classproperty(property): def __get__(self, cls, owner): return classmethod(self.fget).__get__(None, owner)()
Usage:
class Stats: _current_instance = None @classproperty def singleton(cls): if cls._current_instance is None: cls._current_instance = Stats() return cls._current_instance
As noted, this way of creating a singleton is not a good design pattern; if that must be done, a metaclass factory is a much better way to do it. I was just excited about the prospect of a class property though, so, there it is.
Following up with what KyleAlanHale wrote:
His example works great, until you try and do:
Stats.singleton = 5
This won't give you an error, it will overwrite the function, so that when you type next
single = Stats.singletonprint single
You'll get
5
I think you're best off using Kyle's answer without the @classproperties decoration.
I guess giving a Python code snippet to show how do property and staticmethod work would be helpful.
Both of them are descriptors which implements __get__ or __set__
property is a data descriptor
class Property(object): "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
And staticmethod is a non-data descriptor
class StaticMethod(object): "Emulate PyStaticMethod_Type() in Objects/funcobject.c" def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f