【问题标题】:Understanding Borg Singleton Pattern in Python理解 Python 中的 Borg Singleton 模式
【发布时间】:2017-07-25 20:11:59
【问题描述】:

我看到了这个 Borg 单例模式代码,但我无法理解我添加到单例对象的新成员是如何附加到 __shared_state = {} 字典中的。

这里是单例代码

class Borg(object):
    _shared_state = {}

    def __new__(cls,*args,**kwargs):
        obj = super(Borg,cls).__new__(cls,*args,**kwargs)
        obj.__dict__ = cls._shared_state
        return obj

class Child(Borg):
    pass

if __name__ == '__main__':
    borg = Borg()
    another_borg = Borg()

    print borg is another_borg
    child = Child()

    borg.only_one_var = "I'm the only one var"    
    print child.only_one_var

所以我的问题是,当对象 borg.only_one_var 被创建时,它是如何附加到 _shared_state 字典中的

【问题讨论】:

    标签: python class oop design-patterns


    【解决方案1】:

    默认情况下,每个实例都有自己的字典,因此将属性分配给一个实例不会影响其他实例。

    但是你可以让一个实例的字典指向一个新的字典,当你在内部这样做时,它将被用来存储项目。

    在您的情况下,每次创建实例时,您都会将其字典分配为指向Borg. _shared_state。因此,它的所有实例都将使用相同的 dict 来获取和设置属性。

    基本上相当于:

    shared = {}
    
    class A(object):
        def __init__(self):
            self.__dict__ = shared
    

    演示:

    >>> ins = [A() for _ in range(5)]
    >>> ins[0].x = 100
    >>> for i in ins:
    ...     print(i.x)
    ...
    100
    100
    100
    100
    100
    
    >>> shared
    {'x': 100}
    

    在 CPython 中,将新字典分配给 __dict__ 发生在 PyObject_GenericSetDict 内部:

    int
    PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
    {
        PyObject **dictptr = _PyObject_GetDictPtr(obj);
        ...
        if (!PyDict_Check(value)) {
            PyErr_Format(PyExc_TypeError,
                         "__dict__ must be set to a dictionary, "
                         "not a '%.200s'", Py_TYPE(value)->tp_name);
            return -1;
        }
        Py_INCREF(value);
        Py_XSETREF(*dictptr, value);  # Set the dict to point to new dict
        return 0;
    }
    

    请注意,自从key-sharing dictionaries 在 Python 3.3+ 中出现后,同一类实例的字典可以共享一些内部状态以节省空间。

    【讨论】:

      【解决方案2】:

      在类外上下文字典的简单情况下,您可能知道,您可以附加新的键值对,例如:

      dic = {}
      dic['first'] = 1
      print(dic)
      >>> {'first': 1}
      

      在您的情况下,您将 _shared_state 字典分配给对象的字典 obj.__dict__。所以现在obj.__dict__ 是对_shared_state 字典的引用。

      当您在 obj 实例上使用该点路径表示法时,您实际上将一个键值对附加到它的字典中,因此附加到 _shared_state 字典中。

      由于这个类是一个单例,所有其他实例的字典都将引用同一个 _shared_state 字典。因此所有实例都将具有“相同”__dict__

      【讨论】:

      • 不起作用。 TypeError: can't set attributes of built-in/extension type 'dict'
      • 谢谢。写字典和思考对象。傻我:)
      猜你喜欢
      • 2010-11-22
      • 2012-08-21
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多