Ответ, обобщив все обсуждения, с примером. Большое спасибо всем.
Это решение:
- Сохраняет строку документации
- Сохраняет тип
- Поддерживает статические методы и методы класса
Ограничения:
- Синглетоны не могут быть унаследованы, что имеет смысл в контексте
Решение: def singleton (cls):
"""A singleton decorator
Warnings
--------
Singleton decorated classes cannot be inhehited
Example
-------
>>> import abc
>>>
>>> @singleton
... class A():
... "Ad-hoc documentation of class A"
... def __init__(self):
... "Ad-hoc documentation of class A constructor"
... print("constructor called")
... self.x = None
... @classmethod
... def cm(cls):
... "a class method"
... print("class method called")
... def im(self):
... "an instance method"
... print("instance method called")
... @staticmethod
... def sm():
... "a static method"
... print("static method called")
...
>>> @singleton
... class P(abc.ABCMeta):
... @abc.abstractmethod
... def __init__(self):
... pass
...
>>> class C(P):
... def __init__(self):
... print("C1 constructor called")
...
>>> a1 = A()
constructor called
>>> a1.x = 0xCAFE
>>> a1.x
51966
>>> a2 = A()
>>> a2.x
51966
>>> a1.x == a2.x
True
>>> a1 == a2
True
>>> id(a1) == id(a2)
True
>>> type(a1) == type(a2)
True
>>> isinstance(a1, A)
True
>>> ta1 = type(a1)
>>> issubclass(ta1, A)
True
>>> A.cm()
class method called
>>> a1.cm()
class method called
>>> A.sm()
static method called
>>> a1.sm()
static method called
>>> a1.im()
instance method called
>>> try:
... C()
... except Exception as e:
... type(e)
...
<class 'TypeError'>
"""
_instances = {}
_constructorCalled = []
class inner(cls):
def __new__(subcls):
if subcls not in _instances:
_instances[subcls] = cls.__new__(subcls)
return _instances[subcls]
def __init__(self):
if type(self) not in _constructorCalled:
cls.__init__(self)
_constructorCalled.append(type(self))
__init__.__doc__ = cls.__init__.__doc__
__new__.__doc__ = cls.__new__.__doc__
if __new__.__doc__ == (
"Create and return a new object. "
"See help(type) for accurate signature."
):
__new__.__doc__ = "Returns a singleton instance"
inner.__doc__ = cls.__doc__
return inner