如何在Python中将一个类的对象的创建限制为另一个类的实例?

时间:2016-08-15 05:01:53

标签: python class encapsulation

我遇到了一个难题。我想我应该知道如何解决这个问题 - 我在Python方面知识渊博且经验丰富 - 但我无法弄明白。似乎我正在努力解决的问题应该通过一种设计模式来解决,这种设计模式是工厂主题的变体,但我不记得了。 (在这篇文章的最后,我提出了一个技术解决方案,但它似乎是一个难以置信的解决方案。)我希望你发现这个讨论本身很有趣,但我期待听到一些解决我问题的建议。

给定类A,B,C和D,我想将B实例的创建限制为A的方法,将C实例的方法限制为B的方法,将D实例的创建限制为C的方法 - 换句话说,实例是B实例的工厂,B实例为C实例的工厂,C实例为D实例的工厂。 (我还希望D的每个实例存储C的哪个实例创建它,C的每个实例存储B创建它的哪个实例,以及B的每个实例存储A的哪个实例创建它,但这很容易通过让每个实例在层次结构中创建下一个类的实例时将自己提供为__init__参数。)

这些是应用程序操作的模型层类。应用程序操作B,C或D的实例没有问题,它只是不应该直接创建它们 - 它应该能够直接创建A的实例(而A甚至可能是单例)。可以在创建下一个实例的一个类的方法中实现各种类型的验证,管理,跟踪,审计等。

示例可能是可以创建可以创建可以创建文件的目录的文件系统的操作系统,但应用程序代码必须遵循该链。例如,即使它掌握在Directory上,也不应该创建一个File File.__init__ Directory实例,即使这是当要求创建文件时Directory的实例。< / p>

我正在寻找一个支持一些实现的设计解决方案,而不是用户证明的东西 - 我理解后者是徒劳的。

到目前为止,我唯一想到的是:

  1. 开始所有班级名称,但A带有下划线
  2. 仅从A实例访问_B(),仅从B实例访问_C(),仅从_C实例访问_D()
  3. 依赖应用层程序员来尊重这种安排,并直接创建(可能是单例)A类的实例
  4. 通过从模块的__all__列表中省略类来模块级“隐藏”是不够的,因为仅影响import *构造 - 另一个模块仍然可以通过import module到达类引用module.class

    (这一点都模糊地让人联想到需要两个类成为朋友的C ++问题,因为它们参与双向关系:每个类的实例必须能够引用管理另一个类的另一个类的方法关系的一面。)

    最符合Python语义和语用的解决方案是在C中定义D,在B中定义C,在A中定义B.这似乎是一种非常丑陋的方式将多个模块的代码编码到一个单独的非常大的课程。 (并想象一下,如果连锁经历了几个级别。)这是嵌套类的更常见用法的扩展,也许是这种技术上合理的方式,但我从未见过这种俄式玩偶类结构。

    想法?

2 个答案:

答案 0 :(得分:6)

Python并不擅长完全隐藏它的对象... 甚至如果您决定使用“俄罗斯玩偶”封闭来尝试隐藏类定义,用户仍然可以使用它...

class A(object):
    def create_b(self):
        class B(object):
            pass
        return B()

a = A()
b = a.create_b()
b_cls = type(b)
another_b = b_cls()

简单地说 - 如果用户有权访问源代码(他们可以通过inspect或至少获得它,他们可以通过dis获取字节码如果他们有足够的动力,那么他们就有能力自己创建班级的实例。

一般来说,仅仅将类记录为用户不应该自己实例化的东西就足够了 - 如果他们打破了这种信任,那么当他们的程序崩溃和烧伤(并且有一些令人讨厌的副作用)时,这是他们的错这会导致他们的计算机在清除硬盘后着火)。这是一个非常重要的中心蟒蛇哲学,在python mail archives中很好地说明了:

  

python中没有什么是私有的。没有类或类实例可以   让你远离所有内在的东西(这使得内省   可能和强大的)。 Python信任你。它说“嘿,如果你想要的话   在黑暗的地方逛逛,我会相信你有   一个很好的理由而且你没有惹麻烦。“

     

毕竟,我们都是在这里同意成年人。

     

C ++和Java没有这种理念(不同程度)。   它们允许您创建私有方法和静态成员。

     

Perl文化在这方面就像python,但Perl表达了   情绪有点不同。正如骆驼书所说,

     

“Perl模块更愿意你离开它的起居室     因为你没被邀请,不是因为它有霰弹枪。“

     

但情绪是一样的。

答案 1 :(得分:1)

从我的头顶开始:

def B(object):
    pass

def D(object):
    pass

def bound(object):
    if type(object) is C:
        assert isinstance(D)
    if type(object) is A:
        assert isinstance(B)
    else:
        assert false

@bound
def C(D):
    pass

@bound
def A(B):
    pass