Search code examples
python-3.xoopdesign-patternssingletonmetaclass

How to define methods and member variables in class defined with custom metaclass in python


I am defining a singleton class, and using that class as a metaclass to create new classes.

class Singleton(type):
    _lock: Lock = Lock()
    _instance = {}

    def __call__(cls, *args, **kwargs):
        with cls._lock:
            if cls not in cls._instance:
                _instance = super().__call__(*args, **kwargs)
                cls._instance[cls] = cls

        return cls._instance.get(cls)

and the new class is defined like below

class SomeClass(metaclass=Singleton):
    def __init__(self, some_list = []):
         self.some_list = some_list

    def add_to_list(self, a):
         self.some_list.append(a)


some_class = SomeClass()

I am not able to access some_list variable of some_class object. It throws invalid attribute error.

some_class.some_list

a_list = [1,2,4,5]
for l in a_list:
    some_class.add_to_list(l)

Also, I am not able to call add_to_list fn. It throws missing paramter "a" in the arguments.

Can some one help what I am missing in understanding of metaclass concept.


Solution

  • Your error is here:

          cls._instance[cls] = cls
    

    It should be:

          cls._instance[cls] = _instance
    

    You are storing the class itself on your class registry, not its single instance.

    Before we proceed, I will point another problem your code:

        def __init__(self, some_list = []):
    

    Don't ever put a mutable object (an empty list) as a default parameter for a function or method: every time that function is called, the same object is re-used. In this case, this would be mitigated due to the method being in a singleton class, so this __init__ should run only once, but this is wrong enough. The correct pattern is:

        def __init__(self, some_list = None):
             if some_list is None:
                  some_list = []
    

    This ensures a new, different, list is created each time the method is executed.

    And, another thing, I don't know why this recipe of metaclass to create a singleton got so popular, but it is definitely overkill - I talk about it in some other answers, including Create singleton class in python by taking advantage of meta class , Dill doesn't seem to respect metaclass and Accessing the parameters of a constructor from a metaclass .