首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python设计模式,在类创建上运行方法

Python设计模式,在类创建上运行方法
EN

Stack Overflow用户
提问于 2016-07-07 23:03:28
回答 2查看 997关注 0票数 1

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

代码语言:javascript
复制
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方法时都会重置标志。

我找到了一种可行的解决方案,但我想知道是什么最能做到这一点,因为我不相信这就是它。我做了以下工作:

代码语言:javascript
复制
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()

任何帮助都是非常感谢的,如果需要更多的细节,请告诉我。我是新来的。谢谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-07-08 01:29:03

我认为您的解决方案并没有那么糟糕,因为您必须先填充__dict__,然后才能再次调用init,否则将不止一次地调用firstInitializationMethod()。这在您的示例中肯定会发生,因为有了self.val = arg

但是,如果您的Simpleton在其__init__调用中不执行类实例命名空间中的赋值,那么您的解决方案可能会失败。

更简单和健壮的方法就是使用类属性,如下所示:

代码语言:javascript
复制
class Singleton(Borg):
    _first_initialization = True
    def __init__(self,arg):
        Borg.__init__(self)
        if Singleton._first_initialization:
            firstInitializationMethod()
            Singleton._first_initialization = False

您可以通过使用print替换firstInitializationMethod()并创建一些Simpleton对象来测试这段代码,以确保它只被调用一次。

这是可行的,而且_first_initialization不会在每个__init__调用中被重写回True,因为类名称空间是,将与类实例命名空间分离,而Borg只会影响后者(即使Simpleton的所有实例使用相同的__dict__)。

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

考虑使用以下两种方法的代码,其中SingletonSelfless是使用Singleton._first_initialization的,tinker()只是返回self.__first_initialization

代码语言:javascript
复制
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())

及其产出:

代码语言:javascript
复制
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变量的值有明显的区别。

答案很简单。即使类名称空间和类实例命名空间是分开的,实例仍然可以访问类命名空间。但是它只是作为回退类-类实例命名空间具有绝对优先级-但是当它在自己的实例名称中找不到名称时,它就尝试使用类1。让我们来看看__init__在这个Singleton

代码语言:javascript
复制
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__,因此在以后的init调用中,类实例名称空间中将有一个_first_initialization (在第一次__init__调用时使用值False创建的名称空间),并且我们的条件语句将按我们希望的那样解析--而不是执行另一个firstInitializationMethod() (这里是用于演示查询的打印)。

但是,驻留在类命名空间中的原始_first_initialization保持不变。这就是我们得到True False False False的原因。

SingletonSelfless中,我们从不在类实例中创建_first_initialization,所以tinker()调用将回到类命名空间。这就是为什么有4个错误-所有调用指向同一个对象(SingletonSelfless._first_initialization bool变量)。

Singleton中,我们有两个不同的对象--一个来自类命名空间,另一个来自类实例名称空间,在实例之间共享。那么,为什么使用Singleton.而不是self.呢?好吧,对于第一个开始,我们“节省”了难以置信的小内存,在那里只有一个_first_initialization的bool!但真正的原因是很难意外地更改隐藏在类命名空间中的变量。如果我们正在使用self._first_initialization,并且在代码的后面某个地方发生了类似的事情--无论出于什么原因(或者Borg的_shared_dict将被清除或更改,会影响驻留在其中的_shared_dict):a._first_initialization = 'Lol'、Singleton或其子方法self._first_initialization = 'ROFL',那么我们在处理新的Singleton对象时会遇到一些严重的问题。对于Singleton._first_initialization,它会很好,因为init使用的变量只能由显式Singleton._first_initialization='bad idea'修改

票数 1
EN

Stack Overflow用户

发布于 2018-05-24 10:20:38

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

代码语言:javascript
复制
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__方法,传递实例化参数。例如

代码语言:javascript
复制
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
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38256609

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档