Note that there are some explanatory texts on larger screens.

plurals
  1. POCreating a singleton in Python
    text
    copied!<p><em>This question is not for the discussion of whether or not the <a href="http://en.wikipedia.org/wiki/Singleton_pattern" rel="noreferrer">singleton design pattern</a> is desirable, is an anti-pattern, or for any religious wars, but to discuss how this pattern is best implemented in Python in such a way that is most pythonic. In this instance I define 'most pythonic' to mean that it follows the 'principle of least astonishment'</em>.</p> <p>I have multiple classes which would become singletons (my use-case is for a logger, but this is not important). I do not wish to clutter several classes with added gumph when I can simply inherit or decorate.</p> <p>Best methods:</p> <hr> <h2>Method 1: A decorator</h2> <pre><code>def singleton(class_): instances = {} def getinstance(*args, **kwargs): if class_ not in instances: instances[class_] = class_(*args, **kwargs) return instances[class_] return getinstance @singleton class MyClass(BaseClass): pass </code></pre> <p>Pros</p> <ul> <li>Decorators are additive in a way that is often more intuitive than multiple inheritance.</li> </ul> <p>Cons</p> <ul> <li>While objects created using MyClass() would be true singleton objects, MyClass itself is a a function, not a class, so you cannot call class methods from it. Also for <code>m = MyClass(); n = MyClass(); o = type(n)();</code> then <code>m == n &amp;&amp; m != o &amp;&amp; n != o</code></li> </ul> <hr> <h2>Method 2: A base class</h2> <pre><code>class Singleton(object): _instance = None def __new__(class_, *args, **kwargs): if not isinstance(class_._instance, class_): class_._instance = object.__new__(class_, *args, **kwargs) return class_._instance class MyClass(Singleton, BaseClass): pass </code></pre> <p>Pros</p> <ul> <li>It's a true class</li> </ul> <p>Cons</p> <ul> <li>Multiple inheritance - eugh! <code>__new__</code> could be overwritten during inheritance from a second base class? One has to think more than is necessary.</li> </ul> <hr> <h2>Method 3: A <a href="https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python">metaclass</a></h2> <pre><code>class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] #Python2 class MyClass(BaseClass): __metaclass__ = Singleton #Python3 class MyClass(BaseClass, metaclass=Singleton): pass </code></pre> <p>Pros</p> <ul> <li>It's a true class</li> <li>Auto-magically covers inheritance</li> <li>Uses <code>__metaclass__</code> for its proper purpose (and made me aware of it)</li> </ul> <p>Cons</p> <ul> <li>Are there any?</li> </ul> <hr> <h2>Method 4: decorator returning a class with the same name</h2> <pre><code>def singleton(class_): class class_w(class_): _instance = None def __new__(class_, *args, **kwargs): if class_w._instance is None: class_w._instance = super(class_w, class_).__new__(class_, *args, **kwargs) class_w._instance._sealed = False return class_w._instance def __init__(self, *args, **kwargs): if self._sealed: return super(class_w, self).__init__(*args, **kwargs) self._sealed = True class_w.__name__ = class_.__name__ return class_w @singleton class MyClass(BaseClass): pass </code></pre> <p>Pros</p> <ul> <li>It's a true class</li> <li>Auto-magically covers inheritance</li> </ul> <p>Cons</p> <ul> <li>Is there not an overhead for creating each new class? Here we are creating two classes for each class we wish to make a singleton. While this is fine in my case, I worry that this might not scale. Of course there is a matter of debate as to whether it aught to be too easy to scale this pattern...</li> <li>What is the point of the <code>_sealed</code> attribute</li> <li>Can't call methods of the same name on base classes using <code>super()</code> because they will recurse. This means you can't customize <code>__new__</code> and can't subclass a class that needs you to call up to <code>__init__</code>.</li> </ul>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload