我正在尝试实现此处找到的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()
非常感谢任何帮助,如果需要更多详细信息,请与我们联系。我是新手。谢谢!
答案 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