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

Mit*_*del 5 python encapsulation class

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

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

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

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

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

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

  1. 开始所有类名,除了带有下划线的A
  2. 仅从A实例访问_B(),仅从B实例访问_C(),仅从_C实例访问_D()
  3. 依靠应用程序层程序员来尊重这种安排,并直接创建只有(可能是单例)A类的实例

通过从模块__all__列表中省略类来模块级"隐藏" 是不够的,因为它只影响import *构造 - 另一个模块仍然可以通过import module引用来到达类module.class.

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

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

想法?

mgi*_*son 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()
Run Code Online (Sandbox Code Playgroud)

简单地说 - 如果用户可以访问源代码(并且他们可以通过inspect或至少获得它,他们可以获得dis经过训练的眼睛足够好的字节码),那么他们就有能力创建实例如果他们有足够的动力,你自己的班级.

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

python中没有什么是私有的.没有类或类实例可以让你远离所有内部(这使得内省成为可能和强大的).Python信任你.它说:"嘿,如果你想在黑暗的地方闲逛,我会相信你有充分的理由而且你没有惹麻烦."

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

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

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

"Perl模块更愿意你没有被邀请,因为你没有被邀请,而是因为它没有霰弹枪而离开了它的起居室."

但情绪是一样的.