I just wrote a singleton base class for a hobby project I'm currently working on. Python is not my first language, which is why I am not sure if everything is done the pythonic way.
I also used multiple resources/tutorials/articles for Python2 and 3. My singleton is supposed to be Python3 compatible. The implementation should be thread safe, which is why I enter a lock while creating the instance.
To allow custom initializations I decided to let the user provide a create_class
function where the object could be created and initialized. The base class is more like a "singleton-factory" than a "classical" singleton where the object itself provides both (logic and singleton).
Please tell me what can I do better, more efficient, and more pythonic.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
this module implements provides a baseclass for
a singleton.
it allows you to simply create singleton objects
by overriding the PyRlSingleton class.
it gives you access to the singleton instance in
a explicit way via a "get_instance()" function.
if you mistakingly try to create your singleton
class it raises an exception telling you that this
is not allowed.
you have to provide a "create_instance()" function
which creates your singleton implementation; this
is automatically called when the singleton is used
the first time.
the creating process is thread safe, so you can use
it in a multithreading environment.
see the "main()" function for a simple example.
have fun!
"""
import _thread # allocate_lock()
class PyRlSingletonMeta(type):
def __call__(cls, *args, **kwargs):
"""
this method disallows creation of a
singleton via a ClassName() call
"""
raise Exception("not allowed, use get_instance()")
class PyRlSingleton(object, metaclass = PyRlSingletonMeta):
__lock = _thread.allocate_lock()
__inst = None
@classmethod
def create_instance(cls):
"""
this is the default create_instance method
you should provide your own via overriding.
"""
raise Exception("create_instance() missing in {0}".format(cls.__name__))
@classmethod
def get_instance(cls):
"""
this classmethod returns always the same instance
of cls. it creates an object via calling the
create_instance class method when it is first
called.
"""
if cls.__inst == None:
with cls.__lock:
if cls.__inst == None:
cls.__inst = cls.create_instance()
return cls.__inst
def main():
""" test the singleton in a simple example, a logger """
class LoggerImpl(object):
""" my class supposed to be a singleton """
def log(self, text):
print(text)
class Logger(PyRlSingleton):
"""
the singleton itself. i have to override the
create_instance() class method and let it return
my LoggerImpl.
"""
@classmethod
def create_instance(cls):
""" create and initialize your implementation here. """
return LoggerImpl()
Logger.get_instance().log("hello world")
l1 = Logger.get_instance()
l2 = Logger.get_instance()
if l1 != l2:
raise Exception("what?")
if l1 != Logger.get_instance():
raise Exception("ugh!?")
if __name__ == '__main__':
main()