DEV Community

asaf g
asaf g

Posted on • Originally published at turtle-techies.com on

Python and the Magic Singleton

The Singleton Design Pattern

If you have been writing code for a while now, you must have encountered design patterns.
The most simple of them is the singleton.

From Wikipedia:

The singleton pattern is a software design pattern
that restricts the instantiation of a class to one object.

You should use the singleton pattern when your app needs a globally accessible, lazy loaded object that has only one instance.

Some people might tell you that using a singleton in python is strictly unnecessary. Well, that's just wrong. The necessity of the solution always depends on the problem.

If you only need global accessible object, you can use globals. If you don't care about lazy loading, you can use a module or a "static" class. There's lots of possibilites.

In my opinion - if it fits, and it makes your code nicer, go for it.

From the Zen of Python:

"Beautiful is better than ugly."

"If the implementation is easy to explain, it may be a good idea."

Implementation

If you google "python singleton", you will find a lot of different implementations for the singleton pattern.
This one is my favorite:


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]

Enter fullscreen mode Exit fullscreen mode

A Metaclass can customize another class's creation process. It can be a bit confusing, as it is one of the magic features that python has and most programming languages doesn't have. Basically, it's the class of the class. Confused? Let's perform a little experiment and try to understand what's going on.

It's Magic!

Let's try the following code:



class MyMeta(type):

    def __call__(cls, *args, **kwargs):
        print('meta!')


class MyClass(metaclass=MyMeta):

    def __init__(self):
        print('class')


if __name__ == '__main__':
    c = MyClass()
    print(c)


Enter fullscreen mode Exit fullscreen mode

Here, we defined a metaclass that prints out 'metaclass' and a class that prints class.
Note that we are overriding the magic function __call__. The __call__ function executes whenever you call on object. In aspect of classes this means that MyClass() is equivalent to MyClass.call().

The code above outputs the following:


meta!
None

Enter fullscreen mode Exit fullscreen mode

So.. Why is the variable c actually None... ?

This happens because our __call__ function doesn't return anything.
Pretty cool right? You can prank your friends and make their code not work until they find the metaclass definition ( please don't! :) )

Let's go ahead and fix that __call__ function:



class MyMeta(type):

    def __call__(cls, *args, **kwargs):
        print('meta!')
        return super(MyMeta, cls).__call__(*args, **kwargs)  # call the class's __call__ manually


class MyClass(metaclass=MyMeta):

    def __init__(self):
        print('class')


if __name__ == '__main__':
    c = MyClass()
    print(c)

Enter fullscreen mode Exit fullscreen mode

This code outputs:

meta!
class
<__main__.MyClass object at 0x7f43fa218898>

Enter fullscreen mode Exit fullscreen mode

Success! we customized the class instantiation process. Now, we can understand our Singleton metaclass much better. The metaclass makes a single instance and returns that whenever you call the class.

You can do whatever comes to mind with __call__ functions, Python is not very restrictive. However, you should be very careful. As we know, code is more read than written and intuitivity is important.

When Magic Becomes Voodoo

There's a downside to the metaclass singleton implementation, it's implicit.
Consider the following example (MyClass is our singleton from before):


# lots of code here

c = MyClass()

def some_function():
    # some code
    a = MyClass()
    # some more code

# lots of code

class AnotherClass(object):

    def __init__(self, *args):
        self._d = MyClass()
        # more code

# you got it.. more code

Enter fullscreen mode Exit fullscreen mode

We are using our new singleton implementation in a large project, maybe even tens of thousands of lines of code. Now, a new coder joins the team and reads the code.
Do you think he/she will be able to easily tell that MyClass is a singleton?

The answer to that is no in 99% of the cases.

So, what can we do to make our singleton more explicit?

Here is one solution:


class MyClass(metaclass=MyMeta):

    def __init__(self):
        print('class')

    @classmethod
    def get_instance(cls):
        return MyClass()

# instead of calling MyClass() we'll call MyClass.get_instance()

Enter fullscreen mode Exit fullscreen mode

The get_instance class method makes it very clear that this is in fact a singleton.
Sure, you could call MyClass() and it would give you the same result. However, making your code more explicit usually makes it more easy to understand.

Summary

I hope you share my opinions about singletons, magic and voodoo.
Of course if you don't I will be very happy to hear about it.

I hope you enjoyed, and as always, feel free to comment or share.

Thank you for reading.

Top comments (0)