Python – Singletons do not work in Cython

Singletons do not work in Cython… here is a solution to the problem.

Singletons do not work in Cython

This is how I define singletons.

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]

Then I define my class as:

class MyClass(object):
    __metaclass__ = Singleton

def __init__(self):
        pass

a = MyClass()
b = MyClass()

“a is b” returns True

But,

cdef class MyCythonClass(object):
    __metaclass__ = Singleton

def __cinit__(self):
        pass

c = MyCythonClass()
d = MyCythonClass()

“c is d” returns False

I

think this is c code (cinit) running before init, so I tried putting cinit back into init and it doesn’t work either.
If I remove “cdef” before “class”, the problem is solved

I

wonder why, maybe I’m missing something important here. Thank you very much for your help.

Win 10/Python 2.7

Solution

Cython doesn’t seem to support metaclasses out of the box. However this module may provide a workaround.

Implementing a singleton as follows may be another (safer) option:

cdef class Singleton:
    _instances = {}

@classmethod
    def instance(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = cls(*args, **kwargs)
        return cls._instances[cls]

cdef class MyCythonClass(Singleton):
    pass

c = MyCythonClass.instance()
d = MyCythonClass.instance()
c is d  # True

Related Problems and Solutions