这个问题建立在前面提出的这个问题和这个问题的基础上,但解决了一个更微妙的问题,具体来说,什么算作“内部类”?
这是我的情况。我正在构建一个用于操作 MS Office 文件的 Python 库,其中许多类并不打算在外部构建,但它们的许多方法是 API 的重要部分。例如:
class _Slide(object):
def add_shape(self):
...
_Slide
不是在外部构建的,作为库的最终用户,您可以通过调用Presentation.add_slide()
. 但是,一旦有了幻灯片,您肯定希望能够调用add_shape()
. 所以方法是 API 的一部分,但构造函数不是。这种情况在库中出现了数十次,因为只有 Presentation 类具有外部/API 构造函数。
PEP8 在这一点上是模棱两可的,它提到了“内部类”而没有详细说明什么是内部类。在这种情况下,我想可以说这个类是“部分内部的”。
问题是我只有两个符号来区分至少三种不同的情况:
- 类的构造函数和“public/API”方法都可以使用。
- 不使用构造函数,但类的“公共/API”方法是。
- 该类确实是内部的,没有公共 API,我保留在未来版本中更改或删除它的权利。
我注意到,从沟通的角度来看,外部 API(图书馆最终用户受众)和内部 API(开发人员受众)的清晰表达之间存在冲突。对于最终用户来说,呼叫_Slide()
是禁止/自担风险的。然而,它是内部 API 的一个快乐成员,并且_random_helper_method()
只能从内部调用_Slide()
。
你对这个问题有什么看法可能对我有帮助吗?惯例是否规定我在类名上使用“单前导下划线”弹药来争取最终用户 API 的清晰性,或者我是否可以保留它以便与自己和其他开发人员就类 API 何时真正存在进行沟通?私有并且不被它所在模块之外的对象使用,因为它是一个可能会改变的实现细节?
更新:经过几年的进一步思考,我已经习惯了在命名不打算作为模块(文件)之外的类访问的类时使用前导下划线。这种访问通常是实例化该类的对象或访问类方法等。
这为模块的用户(当然通常是您自己 :) 提供了基本指示符:“如果带有前导下划线的类名出现在 import 语句中,则说明您做错了。” (单元测试模块是这个规则的一个例外,这样的导入可能经常出现在“内部”类的单元测试中。)
请注意,这是对类的访问。在模块外访问该类(类型)的对象(可能由工厂或其他任何东西提供)非常好,并且可能是预期的。我认为这种无法将类与从它们创建的对象区分开来是导致我最初感到困惑的原因。
这个约定还有一个好处,就是在from module import *
声明时不包括这些类。尽管我从不在自己的代码中使用这些并建议避免使用它们,但这是一种适当的行为,因为这些类标识符不打算成为模块接口的一部分。
这是我经过多年试验后的个人“最佳实践”,当然不是“正确”的方式。你的旅费可能会改变。