变量和方法中的单下划线与双下划线比较

152 投票
3 回答
109574 浏览
提问于 2025-04-16 22:49

有人好心给我解释过__method()是怎么处理的,但我不想再打扰他,因为还有很多其他人需要帮助。我想知道有没有人能进一步详细说明一下它们之间的区别。

比如,我并不需要混淆,但_这个符号是让它保持私有吗?这样别人就不能用实例去调用instance._method()吗?还是说它只是通过让名字独特来防止覆盖其他变量?我并不需要我的内部方法“隐藏”,但因为它们是特定用途的,我不想让它们在类外被使用。

3 个回答

12

在Python中没有访问控制的概念。你可以访问一个类的所有属性,包括那些被“混淆”的名字(比如 _class__variable)。与其花时间去保护开发者不犯错,不如专注于你的代码和API设计。

133

一个前面有单个下划线的变量名只是个约定,意思是“你可能不应该使用这个。” 这并不会阻止别人使用这个属性。

而前面有两个下划线的变量名则会改变这个属性的名字,这样在继承关系中两个类可以使用相同的属性名,而不会发生冲突。

294

来自 PEP 8 的内容:

  • _single_leading_underscore:表示“内部使用”的弱指示。例如:

    from M import *

    这条语句不会导入名字以下划线开头的对象。

  • single_trailing_underscore_:按照约定使用,避免与Python关键字冲突,例如:

    Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore:在给类属性命名时,会触发名字改写(在类 FooBar 中,__boo 会变成 _FooBar__boo;见下文)。

  • __double_leading_and_trailing_underscore__:表示“魔法”对象或属性,这些对象存在于用户控制的命名空间中。例如 __init____import____file__。永远不要自己发明这样的名字;只在文档中使用它们。

另外,来自David Goodger的 像Pythonista一样编码

属性:interface_internal__private

但尽量避免使用 __private 这种形式。我从不使用它。相信我。如果你使用它,之后一定会后悔。

解释:

来自C++/Java背景的人特别容易过度使用或误用这个“特性”。但是 __private 名称的工作方式与Java或C++不同。它们只是触发了名字改写,目的是为了防止在子类中意外的命名空间冲突:MyClass.__private 变成 MyClass._MyClass__private。(注意,即使这样在不同模块中有相同名称的子类也会出现问题。)可以从类外访问 __private 名称,只是这样做不方便且不稳定(这会依赖于超类的确切名称)。

问题在于,类的作者可能合理地认为“这个属性/方法名称应该是私有的,只能在这个类定义内部访问”,于是使用了 __private 的约定。但后来,这个类的用户可能会创建一个子类,确实需要访问这个名称。因此,要么超类必须被修改(这可能很困难或不可能),要么子类代码必须使用手动改写的名称(这在最好的情况下也很丑陋且不稳定)。

Python中有一个概念:“我们都是成年人”。如果你使用 __private 这种形式,你是在保护这个属性不被谁访问呢?子类有责任正确使用超类的属性,而超类有责任正确记录它们的属性。

使用单下划线开头的约定 _internal 更好。“这并没有被改写;它只是告诉其他人‘小心点,这是内部实现细节;如果你不完全理解它,就不要碰它’。”不过这只是一个约定。

撰写回答