Singleton metaclass example

Here’s a metaprogramming example that I conceived, involving the use of a metaclass to implement the singleton property.

class Counter(object):

    global_count = 0
    count_by_class = {}

    @staticmethod
    def count(obj):
        cls = obj.__class__
        class_count = Counter.count_by_class.get(cls, 0)
        result = (Counter.global_count, class_count)
        Counter.global_count += 1
        Counter.count_by_class[cls] = class_count + 1
        return result

class CountedObject(object):

    def __init__(self):
        print "Creating instance of class %s" % self.__class__.__name__
        (self.big_count, self.small_count) = Counter.count(self)

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return "<%s instance. Global ID: %d. %s ID: %d.>" % (self.__class__.__name__, self.big_count, self.__class__.__name__, self.small_count)

class SingletonMeta(type):

    def __init__(cls, name, bases, d):
        print "Creating class %s" % name
        type.__init__(cls, name, bases, d)

    def __call__(cls):
        print "Attempting to create instance of class %s" % cls.__name__
        if not hasattr(cls, "SINGLETON"):
            instance = type.__call__(cls)
            cls.SINGLETON = instance
        else:
            print "Returning singleton of class %s" % cls.__name__
            instance = cls.SINGLETON
        return instance

class SingletonBase(CountedObject):
    __metaclass__ = SingletonMeta

class FooSingleton(SingletonBase):
    pass

class BarSingleton(SingletonBase):
    pass

class NotSingleton(CountedObject):
    pass

Now, time for some queries:

Creating class SingletonBase
Creating class FooSingleton
Creating class BarSingleton

>>> FooSingleton()
Attempting to create instance of class FooSingleton
Creating instance of class FooSingleton
<FooSingleton instance. Global ID: 0. FooSingleton ID: 0.>

>>> FooSingleton()
Attempting to create instance of class FooSingleton
Returning singleton of class FooSingleton
<FooSingleton instance. Global ID: 0. FooSingleton ID: 0.>

>>> NotSingleton()
Creating instance of class NotSingleton
<NotSingleton instance. Global ID: 1. NotSingleton ID: 0.>

>>> NotSingleton()
Creating instance of class NotSingleton
<NotSingleton instance. Global ID: 2. NotSingleton ID: 1.>

>>> NotSingleton()
Creating instance of class NotSingleton
<NotSingleton instance. Global ID: 3. NotSingleton ID: 2.>

>>> SingletonBase()
Attempting to create instance of class SingletonBase
Creating instance of class SingletonBase
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

>>> SingletonBase()
Attempting to create instance of class SingletonBase
Returning singleton of class SingletonBase
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

>>> BarSingleton()
Attempting to create instance of class BarSingleton
Returning singleton of class BarSingleton
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

>>> BarSingleton()
Attempting to create instance of class BarSingleton
Returning singleton of class BarSingleton
<SingletonBase instance. Global ID: 4. SingletonBase ID: 0.>

As you can see, classes that extend Singleton (or more precisely, classes that are metaclassed by SingletonMeta) can only be instantiated once. This can be seen by the control flow implied by the printed messages, and can also be seen by the IDs of the objects returned.
One complication, though – instantiating BarSingleton seems to return the wrong result because its superclass already has its SINGLETON field set.

Advertisements
Explore posts in the same categories: Python, Uncategorized

One Comment on “Singleton metaclass example”

  1. Dave Cooper Says:

    Have you look at python’s __new__() special method? It’s a little more terse and readable.

    I wrote something similar for the RPC module a few months ago: https://www.drproject.org/DrProject/browser/trunk/drproject/rpc?rev=4878&file=siblings.py#L19


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: