为什么python在某些事情上使用两个下划线?

我对实际编程语言相当陌生,Python是我的第一个。 我知道我有一点Linux的方法,足以让暑假工作(我还在读高中),在工作中,我有很多空闲的时间来学习Python。

有一件事一直在让我。 Python中有什么不同,当你有像

x.__add__(y) <==> x+y x.__getattribute__('foo') <==> x.foo 

我知道什么方法和东西,我得到他们做什么,但我的问题是:这些双下划线方法是如何不同于他们简单的看起来相等?

PS,我不介意在编程历史上讲课,事实上,我觉得它是非常有用的知道:)如果这些主要是Python的历史方面,随意开始散漫。

那么,程序员的权力是好的,所以应该有一种方法来定制行为。 像运算符重载( __add__ __getattribute____getattr__ div __getattribute____getattr__ ge __getattribute__ ,…),属性访问( __getattribute____getattr__ getattr __getattr__ (这两者是不同的), __getattr__ delattr __getattr__ ,…)等等。在很多情况下,像运算符一样,语法映射1:1到相应的方法。 在其他情况下,有一个特殊的过程,在某些时候涉及调用相应的方法 – 例如,只有当对象没有被请求的属性,并且__getattribute__没有实现或者引发AttributeError时, __getattr__才被调用。 其中一些是真正的高级主题,它们让你能够进入对象系统的内核,而且很less需要。 所以不需要全部学习,只需在需要/想知道的时候参考参考。 说到参考, 这里是 。

这是Python的创build者解释它 :

…而不是为特殊种类的方法(如初始化和析构函数)devise一个新的语法,我决定这些特性可以通过简单地要求用户实现具有特殊名称的方法来处理,如__init____del__等向前。 这个命名约定是从C获取的,其中以下划线开头的标识符由编译器保留,并且通常具有特殊含义(例如,C预处理器中的macros__FILE__ )。

我也使用这种技术来允许用户类重新定义Python操作符的行为。 如前所述,Python是用C实现的,它使用函数指针表来实现内置对象的各种function(例如“get attribute”,“add”和“call”)。 为了允许这些function在用户定义的类中定义,我将各种函数指针映射为特殊的方法名,例如__getattr____getattr__ __add____call__ 。 在C中实现新的Python对象时,这些名字和函数指针表之间有一个直接的对应关系。

当你开始一个有两个下划线的方法(并且没有尾随下划线)时,Python的名字改变规则被应用。 这是从其他OO语言(如C ++和Java)松散地模拟private关键字的一种方法。 (即便如此,该方法在技术上仍然不是私有的,Java和C ++方法是私有的,但是从实例外部“难以获得”)。

具有两个前导和两个尾随下划线的方法被认为是“内置的”方法,即它们被解释器使用,并且通常是重载操作符或其他内置function的具体实现。

它们用于指定Python解释器应该在特定情况下使用它们。

例如, __add__函数允许+运算符为自定义类工作。 否则在尝试添加时会出现某种未定义的错误。

从历史的angular度来看,主要的下划线通常被用作向程序员指示名称被认为是内部定义它们的包/模块/库的方法。 在对私有名称空间不提供良好支持的语言中,使用下划线是一种惯例来模拟。 在Python中,当你定义一个名为'__foo__'的方法时,维护程序员从名字中知道一些特殊的事情正在发生,这种事情并没有发生在名为'foo'的方法中。 如果Pythonselect使用'add'作为内部方法来重载'+',那么你就不会有一个方法'add'的类而不会造成太大的混淆。 下划线提示有一些魔法会发生。

现在还有一些其他的问题被标记为这个问题的重复,至less有两个问题是要么调用__spam__方法,要么调用什么约定,现有的答案都没有涵盖,所以:

其实也没有正式的名字。

许多开发者非正式地将他们称为“dunder方法”,称为“双UNDERscore”。

有些人使用术语“神奇的方法”,但在意义上的方法,特殊的方法(见下文)或两者之间的某种东西之间有些模棱两可。


有一个“特殊属性”的官方术语,与dunder方法密切相关但不完全相同。 参考文献中的“ 数据模型”一章从来没有完全解释过什么特殊的属性,但基本思想是它至less是下列之一:

  • 由解释器本身或其内置代码提供的属性,如__name__上的__name__
  • 一个属性是由解释器本身实现的协议的一部分,如+运算符的__add__或索引和切片的__getitem__
  • 允许解释器专门查找的属性,通过忽略实例并右移到类,如__add__

大多数特殊属性是方法,但不是全部(例如, __name__不是)。 大多数情况下使用“dunder”约定,但不是全部(例如Python 2.x中迭代器的next方法)。

同时,大多数dunder方法都是特殊的属性,但并不是全部 – 特别是stdlib或外部库不希望定义自己的协议,像pickle协议一样工作。

[猜测] Python受到Algol68的影响, Guido可能在阿姆斯特丹大学使用Algol68,Algol68有一个类似的“ stropping政权 ”,称为“引用stropping”。 在Algol68中, 运算符,types和关键字 可以以不同的字体出现(通常是** bold **或__underlined __),在源代码文件中,这个字体是用引号实现的,例如'abs'(引用类似于' wikitext ' )

Algol68⇒Python(映射到成员函数的运算符)

  • '和'⇒__and__
  • '或'⇒__or__
  • '不'⇒不
  • 'entier'⇒__trunc__
  • 'shl'⇒__lshift__
  • 'shr'⇒__rshift__
  • 'upb'⇒__sizeof__
  • 'long'⇒__long__
  • 'int'⇒__int__
  • '真实'⇒__float__
  • 'format'⇒__format__
  • 'repr'⇒__repr__
  • 'abs'⇒__abs__
  • 'minus'⇒__neg__
  • 'minus'⇒__sub__
  • 'plus'⇒__add__
  • 'times'⇒__mul__
  • 'mod'⇒__mod__
  • 'div'⇒__truediv__
  • 'over'⇒__div__
  • 'up'⇒__pow__
  • 'im'⇒形象
  • 're'⇒真实的
  • 'conj'⇒结合

在Algol68中,这些被称为粗体名称 ,例如abs ,但在python中是“under-abs-abs”__abs__。

我的2分钱:¢所以有时候 – 就像幽灵一样 – 当你把python类剪切并粘贴到wiki中时,你将会神奇地转化Algol68的大胆的关键字。 ¢