Python Borg设计模式,创建类的运行方法

时间:2016-07-07 23:03:29

标签: python variables design-patterns

我正在尝试实现此处找到的Borg设计模式(在下面重新创建):http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html

class Borg:
    _shared_state = {}
    def __init__(self):
        self.__dict__ = self._shared_state

class Singleton(Borg):
def __init__(self, arg):
    Borg.__init__(self)
    self.val = arg
def __str__(self): return self.val

我想在这个类的第一次初始化时运行一个特定的方法,但是再也不会。最初,我尝试使用一些布尔标志,但根据我的理解,Singleton类被初始化多次,但状态和行为在所有实例中都很常见。因此,我在 init 方法中执行的任何初始化都会多次发生,因此每次初始化Singleton方法时都会重置标志。

我找到了一个有效的解决方案,但我想知道最狡猾的方法是什么,因为我不相信这就是它。我做了以下事情:

class Singleton(Borg):
def __init__(self, arg):
    Borg.__init__(self)
        if not self.__dict__: #I'm just checking that the namespace is empty, knowing it will be filled with something in the future. 
            firstInitializationMethod()

非常感谢任何帮助,如果需要更多详细信息,请与我们联系。我是新手。谢谢!

2 个答案:

答案 0 :(得分:1)

我认为您的解决方案并不是那么糟糕,因为您必须在再次调用下一个__dict__之前填充init,或者调用firstInitializationMethod()以外的self.val = arg一旦。由于__init__,保证在您的示例中发生。

但是,如果您的Simpleton在其class Singleton(Borg): _first_initialization = True def __init__(self,arg): Borg.__init__(self) if Singleton._first_initialization: firstInitializationMethod() Singleton._first_initialization = False 调用中不在类实例命名空间内进行分配,那么您的解决方案可能会失败。

更简单,更健壮的方法就是使用类属性:

firstInitializationMethod()

您可以通过将Simpleton替换为print并创建一些_first_initialization个对象来测试此代码,以确保它只会被调用一次。

这有效,True__init__次调用时不会被覆盖回__dict__,因为类命名空间实例分开 namespace和Borg只影响后者(即使Simpleton的所有实例使用相同的SingletonSelfless)。

后续问题: 我用self而不是Singleton尝试了代码,但它仍然有用。似乎他们决心做同样的事情。是否有理由使用Singleton?

使用这两种方法考虑此代码,其中Singleton._first_initialization是使用tinker()的方法,self.__first_initialization只是返回a = Singleton('a') print(a) b = Singleton('b') print(a,b) c = Singleton('c') print(a,b,c) print(Singleton._first_initialization, a.tinker(),b.tinker(),c.tinker()) a = SingletonSelfless('a') print(a) b = SingletonSelfless('b') print(a,b) c = SingletonSelfless('c') print(a,b,c) print(SingletonSelfless._first_initialization, a.tinker(),b.tinker(),c.tinker())

doing some init!!
a
b b
c c c
True False False False
doing some init!!
a
b b
c c c
False False False False

及其输出:

_first_initialization

从实际的角度来看,这两种实现都像我们希望的那样工作,但__init__变量值存在明显差异。

答案很简单。 即使类名称空间和类实例名称空间是分开的,实例仍然可以访问类名称空间。 但它只能作为回退 - 类实例命名空间具有绝对优先级 - 但是当它无法在自己的实例namepsace中找到名称时,它会尝试使用第一类。 我们来看看Singleton中的class Singleton(Borg): _first_initialization = True def __init__(self,arg): Borg.__init__(self) if self._first_initialization: print('doing some init!!') self._first_initialization = False self.val = arg def tinker(self): return self._first_initialization def __str__(self): return self.val

_first_initialization

即使实例没有if,我们也会使用Singleton._first_initialization解析self._first_initialization。 但是,将False设置为_first_initialization会在实例命名空间中创建Borg变量。 非常感谢__dict__我们所有的实例共享相同的_first_initialization,因此在后续的init调用中,类实例命名空间中会有__init__(在False调用时创建的firstInitializationMethod()_first_initialization) 我们的条件陈述将按照我们的意愿解决 - 不再做另一个True False False False(此处为演示用语打印)。

然而,我们在类命名空间中的原始SingletonSelfless没有变化。这就是为什么我们得到_first_initialization

SingletonSelfless._first_initialization中,我们永远不会在类实例中创建Singleton,因此tinker()调用将回退到类命名空间。这就是为什么有4个falses - 所有调用都指向同一个对象(Singleton. bool变量)。

self.中,我们有两个不同的对象 - 一个来自类名称空间,另一个来自实例之间共享的类实例名称空间。 那么为什么要使用self._first_initialization代替a._first_initialization = 'Lol'呢?首先我们通过在那里只有一个_first_initialization bool'保存'令人难以置信的微小内存! 但真正的原因是,在类命名空间中意外更改变量隐藏更加困难。 如果我们在我们的代码中使用self._first_initialization = 'ROFL'以后的某个地方,出于某种原因发生了类似的事情(或者Borg的_shared_dict将被清除或更改,会影响到那里): Singleton._first_initialization或单身人士或其子方法Singleton._first_initialization='bad idea' 那么我们在初始化新的Singleton对象时会遇到一些严重的问题。 使用{{1}}它会很好,因为用于init的变量只能通过显式{{1}}修改

答案 1 :(得分:1)

我知道这已经很老了,但我只想到了以下解决方案:

class Borg(object):
    _state = {}
    def __new__(cls, hive_name, *p):
        self = object.__new__(cls, *p)
        if hive_name not in cls._state:
            cls._state[hive_name]={}
            self.__dict__ = cls._state[hive_name]
            self.__init_hive__(*p)
        else:
            self.__dict__ = cls._state[hive_name]
        return self

    def __init_hive__(self, *p):
        pass

此实现允许您创建多个共享状态(与主题保持一致,我称之为这些配置单元)并自由分配实例。创建配置单元时,它会调用配置单元中第一个实例上的__init_hive__方法,并传递实例化参数。例如

class WeAre(Borg):
    def __init__(self, hive_name, arg):
        super(WeAre, self).__init__(hive_name, arg)

    def __init_hive__(self, arg):
        self.hive_arg=arg

# Prints foo
print WeAre("The Collective", "foo").hive_arg

# Prints foo again
print WeAre("The Collective", "bar").hive_arg

# Prints bar
print WeAre("The Hive", "bar").hive_arg