Python class inherited singleton inits instance on every call

这一生的挚爱 提交于 2021-01-29 12:26:33

问题


I'm trying to implement class inherited singleton as described here (Method 2). Going over the question and the extensive chosen answer I tried to implement the following:

class Singleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls, *args, **kwargs)
            cls._instance._initialized = False

        return cls._instance

class A(Singleton):
    def __init__(self):
        print "Init is called"

class B(Singleton):
    def __init__(self):
        print "Init is called"

As you may guess, whenever I create Class A I get the same object, but __init__ is called. This is problematic as the Class A can have all it's members changed due to this.

Doing:

a1 = A()
a2 = A()
print a1 == a2

Will result in:

>> Init is called
>> Init is called
>> True

This question poses a similar issue but I would prefer not to use the solution there as it doesn't include the inheritance and I have at least 2 classes that needs the Singleton inheritance. I tried to implement the solution here but it didn't work. The solution here works but it involves changing Class A and Class B which I would prefer not to.

Is there a way to change the Singleton implementation so that __init__ won't be called on every creation? (I can't use metaclasses as both A and B inherit other classes e.g. abstract classes with their own metaclasses).

Thank you


回答1:


In modern Python this could be done by writting a __init_subclass__ method that could decorate __init__ to make it check cls._instance before running.

As it is, (i.e. you need it for Python 2 as well) - I think the simpler thing is for __new__ to patch __init__ with a NOP method if the instance already exists:


_nop_init = lambda self, *args, **kw: None

class Singleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls, *args, **kwargs)
            cls._instance._initialized = False
        # Python 2 have to check in the cls.__dict__ - Py3 could check the attribute directly:
        elif cls.__dict__.get("__init__", None) is not _nop_init:
            cls.__init__ = _nop_init
        return cls._instance

class A(Singleton):
    def __init__(self):
        print "Init is called"

class B(Singleton):
    def __init__(self):
        print "Init is called"

extra info The language mechanism to call __init__ is built-in the __call__ method of type - the metaclass for all classes in Python. It will call the __new__ and __init__ method of a target class when instantiating it, and thus, with a metaclass, it is easy to control these calls from a custom metaclass. It is also interesting to note that when __new__ does not return an instance of the target class, __init__ is not called. (In this singleton case, the singleton is an instance of te class, and thus it is called).

real world example: last time I coded a singleton, I opted for avoiding re-run __init__ in its own code - sine it is the only singleton in the project, tere was no need for generic code for that in a __new__: https://github.com/jsbueno/pythonchain/blob/1f9208dc8bd2741a574adc1bf745d218e4314e4a/pythonchain/block.py#L276



来源:https://stackoverflow.com/questions/59316642/python-class-inherited-singleton-inits-instance-on-every-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!