I combined @Blckknght's answer with Method 3 from Creating a singleton in Python, using metaclass.
from abc import ABCMeta, abstractmethod
from functools import wraps
import time
class Singleton(ABCMeta):
_instance = None
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance") or cls._instance is None:
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance
class ContextDecorator(object):
__metaclass__ = Singleton
def __init__(self, *args, **kwargs):
self.args = args
self.__dict__.update(kwargs)
self._built = False
self._contextExists = False
@abstractmethod
def _build(self):
pass
@abstractmethod
def _destory(self):
pass
@classmethod
def clear_singleton(cls):
cls._instance = None
def __enter__(self):
if not self._built:
self._build()
self._built = True
print 'call _build first time'
else:
print 'skip _build'
self._contextExists = True
return self
def __exit__(self, typ, val, traceback):
if not self._contextExists:
self._destory()
self.clear_singleton()
# self._contextExists=False
print 'call _destory first time'
else:
print 'skip _destory'
self._contextExists = False
def __call__(self, f):
self.function = f
@wraps(f)
def wrapper(*args, **kw):
with self:
try:
return f(*args, **kw)
except:
raise
return wrapper
class CustomContext(ContextDecorator):
def __init__(self, *args, **kwargs):
super(CustomContext, self).__init__(*args, **kwargs)
def _build(self):
pass
def _destory(self):
pass
print 'context managere test'
with CustomContext():
for i in range(3):
with CustomContext():
time.sleep(0.01)
print '-' * 10
print 'decorator test'
@CustomContext()
@CustomContext()
def test():
print 'in side test func'
test()
And the output
context managere test
call _build first time
skip _build
skip _destory
skip _build
skip _destory
skip _build
skip _destory
call _destory first time
----------
decorator test
call _build first time
skip _build
in side test func
skip _destory
call _destory first time