变量和方法中的下划线和双下划线区别

151

有人很好心地向我解释了 __method() 的名称修饰,但是由于还有很多需要帮助的人,所以我不想再打扰他了。我想知道是否有人能进一步说明它们之间的区别。

例如,我不需要名称修饰,但是如果使用 _,那么 instance._method() 是否无法被外部访问?或者它只是通过使其唯一来防止其他变量被覆盖?我不需要隐藏我的内部方法,但是由于它们特定于使用,我不希望它们在类外被使用。

3个回答

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的Code Like a Pythonista:

属性: interface, _internal, __private

但尽量避免使用 __private。我从来不用它。相信我,如果你使用了它,你以后会后悔的。

解释:

来自 C++/Java 背景的人们特别容易滥用/误用这个“特性”。但是,__private 名称不像在 Java 或 C++ 中那样工作。它们只触发名称混淆,其目的是防止子类中的意外命名空间冲突:MyClass.__private 只变成了 MyClass._MyClass__private。(请注意,即使对于具有与超类相同名称的子类,例如不同模块中的子类,这也会失败。) 可以从其类的外部访问 __private 名称,只是不方便和脆弱(它增加了对超类确切名称的依赖)。

问题在于,类的作者可能会合理地认为“这个属性/方法名称应该是私有的,只能从此类定义内部访问”,并使用 __private 约定。但是,稍后,该类的用户可能会创建一个合法需要访问该名称的子类。因此,要么必须修改超类(可能很困难或不可能),要么子类代码必须使用手动混淆的名称(最多是丑陋和脆弱)。

Python 中有一个概念:“我们都是同意的成年人”。如果您使用 __private 格式,那么您正在保护该属性免受谁的侵害?子类负责正确地使用超类的属性,超类负责正确地记录其属性。

最好使用单个前导下划线约定,_internal。“这根本没有名称混淆;它只是向其他人指示“小心使用这个,它是内部实现细节;如果您不完全了解它,请勿触摸它”。但这只是一个约定。


19
我不是很明白为什么不应该使用私有方法的解释 - 我的意思是,在Java等编程语言中完全可以用同样的论点,但是迄今为止,尽管使用了大量私有方法(不能被子类使用),Java程序仍然能够正常工作。 - Voo
1
@Voo 当然,它们可以工作,但是这很麻烦,而且坦率地说,大多数情况下只会在Java社区中导致不必要的演讲。 - Marcin
12
在其他编程语言中,将一个字段/方法从私有(private)更改为公共(public)只需要进行一行代码的修改(在定义中将"private"关键字改为"public")。但在Python中,您需要同时更改属性名称以及其调用位置。虽然这并不是特别困难,但仍然容易出现错误。 - matiasg
2
我同意“我们都是成年人”,但由于 Python 支持后期绑定,这可能会很危险。您可能会意外覆盖子类的方法,从而破坏代码。没有任何提示会告诉您发生了什么,并且很难找出原因。IDE 应该支持相关的警告。有人知道一个好的 IDE 可以支持此类警告吗?就像 Visual Studio 为 C# 做的那样。 - Ini
1
@Invader pylint - nmichaels
显示剩余3条评论

132

单个前导下划线仅仅是一种惯例,意思是“你可能不应该使用它”。它并不能阻止其他人使用该属性。

双个前导下划线实际上会更改属性名称,这样继承层次结构中的两个类可以使用相同的属性名称,而不会发生冲突。


1
我喜欢这个解释!!虽然简短而且简洁,但是清晰地传达了整个概念。我只会添加一个名称更改的示例:如何在Class1和Class2中使用__foo()变成_Class1__foo()和_Class2__foo()。 - Casey Jones

12

Python 中不存在访问控制。你可以访问类的所有属性,包括变量名被改名后的私有变量(例如 _class__variable)。集中精力编写自己的代码和 API,而不是试图保护开发者免受自身影响。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接