【问题标题】:Python class inherited singleton inits instance on every callPython 类在每次调用时继承单例初始化实例
【发布时间】:2020-04-06 13:21:22
【问题描述】:

我正在尝试按照here(方法 2)的描述实现类继承的单例。回顾了这个问题和extensive chosen answer,我尝试实现以下内容:

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"

正如您可能猜到的,每当我创建 Class A 时,我都会得到相同的对象,但会调用 __init__。这是有问题的,因为 Class A 可能因此而更改其所有成员。

在做:

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

将导致:

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

This question 提出了类似的问题,但我不想在那里使用该解决方案,因为它不包括继承,而且我至少有 2 个需要Singleton 继承的类。我尝试实施解决方案here,但没有奏效。 solution here 有效,但它涉及更改 Class AClass B,我不希望这样做。

有没有办法改变Singleton 的实现,使__init__ 不会在每次创建时都被调用? (我不能使用元类,因为AB 都继承了其他类,例如具有自己的元类的抽象类)。

谢谢

【问题讨论】:

    标签: python python-2.7 inheritance singleton metaclass


    【解决方案1】:

    在现代 Python 中,这可以通过编写一个 __init_subclass__ 方法来完成,该方法可以装饰 __init__ 以使其在运行前检查 cls._instance

    事实上,(即 Python 2 也需要它)- 我认为更简单的方法是 __new__ 如果实例已经存在,则使用 NOP 方法修补 __init__

    
    _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"
    

    额外信息 调用__init__ 的语言机制内置于type__call__ 方法 - Python 中所有类的元类。在实例化目标类时,它将调用目标类的__new____init__ 方法,因此,使用元类,很容易从自定义元类控制这些调用。值得注意的是,当__new__ 没有返回目标类的实例时,__init__ 也不会被调用。 (在这种单例情况下,单例 te 类的一个实例,因此它被调用。

    真实世界示例:上次我编写单例时,我选择避免在自己的代码中重新运行 __init__ - 因为它是项目中唯一的单例,所以没有必要对于 __new__ 中的通用代码: https://github.com/jsbueno/pythonchain/blob/1f9208dc8bd2741a574adc1bf745d218e4314e4a/pythonchain/block.py#L276

    【讨论】:

      猜你喜欢
      • 2017-10-21
      • 2020-01-17
      • 2015-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-22
      • 1970-01-01
      相关资源
      最近更新 更多