0

I'm reading through http://blog.thedigitalcatonline.com/blog/2014/09/01/python-3-oop-part-5-metaclasses/#.VwPSjDG1XGD. In it, they have:

class Singleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if not cls.instance:
             cls.instance = super(Singleton, cls).__call__(*args, **kw)
        return cls.instance

The explanation is:

We are defining a new type, which inherits from type to provide all bells and whistles of Python classes. We override the call method, that is a special method invoked when we call the class, i.e. when we instance it. The new method wraps the original method of type by calling it only when the instance attribute is not set, i.e. the first time the class is instanced, otherwise it just returns the recorded instance. As you can see this is a very basic cache class, the only trick is that it is applied to the creation of instances.

I'm not sure I understand the line:

cls.instance = super(Singleton, cls).__call__(*args, **kw)

Can someone explain what is happening here in another way?

1
  • 2
    The code has a severe error, it should read if cls.instance is None:. Commented Apr 5, 2016 at 16:43

1 Answer 1

3

By default, __call__ing on a class object produces an instance of such class (remember that classes are callables like functions, in contrast to other languages like PHP where they are completely separate monsters in their interface). This instance will be stored in cls.instance.

cls.instance = super(Singleton, cls).__call__(*args, **kw)

By wrapping in the previous condition, if the instance is already an... instance, it is returned. This means: stuff like __new__ is not called on the class again, and stuff like __init__ is not called on the instance again, but just returned the old -already existent- instance.

Notes: When you call __call__ in the default implementation, the __new__ method is called for the class (it is always a class method; you never use the @classmethod decorator there) to create the instance. After that, the __init__ message is sent to the instance to initialize it.

Notes on Metaclasses: Remember that Singleton is not an usual class, but a Metaclass. It is not a class you will inherit from, but a Metaclass you will instantiate your class with.

Objects are instances of Classes, and Classes are instances of Metaclasses (which may confuse you since they are also classes). This means that there's a closed loop in which the reference type is an instance of itself:

assert isinstance(type, type)

There, your code will come like this:

class MySingletonClass(object):
    __metaclass__ = Singleton

And the magic will begin:

  1. The __call__ method you are overriding with your code will be executed on the class since it is defined on the metaclass (Singleton). This means:

    i = MySingletonClass() # Executes what is defined in Singleton as __call__
    

    You should not confuse with this one:

    i() # Will crash if MySingletonClass does not define __call__ for its instances.
    
  2. The call you are making in your code has another equivalent:

    super(Singleton, cls).__call__(*a, **kwa)
    

    Is the same as:

    (a type isntance)(*a, **kwa)
    

    Or

    (a type isntance).__call__(*a, **kwa)
    

    Which is the same behavior every class, not using a custom metaclass like yours, uses to create their instances.

Sign up to request clarification or add additional context in comments.

11 Comments

Thanks, that helps. What about " super(Singleton, cls)" - why is this used?
You are making a super proxy to the parent class to call the __call__ method in the parent class (i.e. in type), which has the default behavior I described. Adding to the answer... Remember that Singleton is not a class you will inherit from, but a Metaclass
Thank you. In " super(Singleton, cls)", I assume cls refers to the specific instance of the Singleton class?
Aha. cls is your class, which is created by Singleton metaclass. Usually, classes which don't have a custom metaclass have a default behavior for call. With that line you are invoking such behavior.
I now have 2 questions based on your last comment (sorry): 1) what do you mean by "super proxy to the parent class" 2) How do we know this is a metaclass?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.