1

I was messing around with metaclasses a little bit trying to figure out how I may use one to create a singleton data structure when I came across something that's stumped me:

Also, I have done my homework and looked at this page and this one too, but I wanted to see if I could do it on my own and the problem that arose is less about how to implement a singleton and more about the functionality of something in my version of a singleton metaclass.

class SingletonError(Exception):
    pass

class Singleton(type):

    def __new__(metacls, name, parents, kwargs):
        cls = super(Singleton, metacls).__new__(metacls, name, parents, kwargs)
        cls._instance = None
        return cls

    def __call__(cls, *args, **kwargs): #cls is the class being called, in this case
                                        #when Quux is called, the Quux class is sent
                                        #as the cls argument.
        if not cls._instance:
            inst = cls.__new__(cls, *args, **kwargs)
            cls._instance = inst
        else:
            raise SingletonError("Cannot initialize multiple singletons")
        print('returning', cls._instance)
        return cls._instance

class Quux(metaclass = Singleton):
    pass

Most of it works, like it does indeed raise a SingletonError when trying to initialize multiple instances of Quux, but watch what happens when I try to create an instance of Quux:

>>> q = Quux()
returning <__main__.Quux object at 0x02BE7E90>
>>> type(q)
<class '__main__.Quux'>

Does this mean that the __call__ method in the Singleton metaclass is returning the class object as opposed to the instance created in the __call__ method? If so, how may I fix it with the current setup I have?

(I realize there are better ways of creating a singleton but for the sake of this exercise and learning about metaclasses I would like to solve it with this setup).


EDIT: Okay, so BrenBarn just pointed out that I made a major derp and thought that when it type returned it was returning the class when really if it were returning the class object it would say . Sorry about the confusion.

But now I have a new problem: lets assume I have a __new__ method for the Quux class.

class Quux(metaclass = Singleton):

    def __new__(cls, thing):
        name = cls.__name__
        parents = cls.__bases__
        kwargs = {'thing' : thing}
        return super(Quux, cls).__new__(cls, thing)

It raises a TypeError saying that object.__new__() takes no parameters. How can I fix this __new__ method to return an instance of the Quux class?

Community
  • 1
  • 1
user3002473
  • 3,984
  • 4
  • 30
  • 53
  • I edited my answer to answer your second question. However, just FYI, it's not usually a good idea to edit your question to add a totally separate question like that. Your question title doesn't reflect your new question, so people who could answer it might not find it. If an answer to a question leads you to a new question that isn't directly an extension/modification of the original question, you should post that as a separate question. – BrenBarn Feb 05 '14 at 05:02
  • Sorry about that, in the future I'll remember to make two separate questions when I have such a major detour from my original question. Thanks for the advice! – user3002473 Feb 05 '14 at 05:06

1 Answers1

1

No, it is returning an instance of Quux. The type of an instance is its class. So the type of your Quux instance is Quux.

This doesn't really have anything to do with your metaclass. It's the normal behavior for any old user-defined class:

>>> class Foo(object):
...     pass
>>> f = Foo()
>>> type(f)
<class '__main__.Foo'>

Foo is a class. f is an instance of Foo, so type(f) is Foo.

To answer your second question: just don't pass thing to the superclass. Just do return super(Quux, cls).__new__(cls).

BrenBarn
  • 210,788
  • 30
  • 364
  • 352