为什么python对某些东西使用两个下划线?

时间:2010-08-09 18:34:46

标签: python double-underscore

我对实际的编程语言相当新,而Python是我的第一个。我知道我在Linux上的方式,足以获得暑期工作(我还在高中),在工作中,我有很多空闲时间,我正在学习Python。

有一件事让我感到高兴。当你有像

这样的表达式时,Python究竟有什么不同
x.__add__(y) <==> x+y
x.__getattribute__('foo') <==> x.foo

我知道什么方法做什么和做什么,我得到他们做的,但我的问题是:上面的双下划线方法与它们更简单的等价方法有何不同?

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

7 个答案:

答案 0 :(得分:28)

以下是Python explaining it的创建者:

  

...而不是设计新的语法   特殊种类的方法(如   作为初始化者和析构者),我   决定这些功能可以   通过简单地要求用户来处理   用特殊方法实现方法   __init____del__和。{   等等。这个命名惯例是   取自C所在的标识符   以下划线开头是保留的   由编译器经常有特殊的   意义(例如,诸如的宏)   C预处理器中的__FILE__

...

  

我也使用这种技术来允许   用户类重新定义行为   Python的运营商。如从前那样   注意到,Python是用C和C实现的   使用函数指针表   实现各种能力   内置对象(例如,“获取   属性“,”添加“和”调用“)。至   允许定义这些功能   在用户定义的类中,我映射了   各种函数指针特殊   方法名称,例如__getattr__,   __add____call__。有一个   这些之间的直接对应   名称和功能表   指针必须定义何时   在C中实现新的Python对象。

答案 1 :(得分:16)

当您使用两个下划线(并且没有尾随下划线)启动方法时,将应用Python的name mangling规则。这是一种从其他OO语言(如C ++和Java)中松散模拟private关键字的方法。 (尽管如此,该方法在技术上仍然不是Java和C ++方法私有的方式,而是从实例外部“更难”获取。)

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

答案 2 :(得分:7)

嗯,程序员的力量很好,所以应该有一种方法来定制行为。与运算符重载(__add____div____ge__,...)类似,属性访问(__getattribute____getattr__(这两者不同),{{ 1}},...)等。在许多情况下,与运算符一样,通常的语法将1:1映射到相应的方法。在其他情况下,有一个特殊的过程,在某些时候涉及调用相应的方法 - 例如,__delattr__仅在对象没有请求的属性且__getattr__未实现或引发了AttributeError。其中一些是非常高级的主题,可以让你深入了解对象系统的内容并且很少需要。所以不需要全部学习它们,只需在需要/想知道时查阅参考文献。说到参考,here it is

答案 3 :(得分:4)

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

例如,__add__函数允许+运算符用于自定义类。否则,在尝试添加时,您将收到某种未定义的错误。

答案 4 :(得分:3)

从历史角度来看,前导下划线经常被用作向程序员指示名称将被视为定义它们的包/模块/库的内部的方法。在不能为私有命名空间提供良好支持的语言中,使用下划线是一种模仿它的惯例。在Python中,当你定义一个名为'__foo__'的方法时,维护程序员会从名称中知道一些特殊情况正在发生,而这种方法并没有发生在名为'foo'的方法上。如果Python选择使用'add'作为重载'+'的内部方法,那么你就永远不会有一个带有'add'方法的类,而不会引起太多混乱。下划线充当了一些神奇的暗示。

答案 5 :(得分:1)

现在将其他一些问题标记为此问题的副本,并且至少有两个问题询问调用__spam__方法或调用约定的内容,并且现有答案均未涵盖那么,所以:

实际上没有正式名称。

许多开发人员非正式称他们为“Dunder方法”,称为“Double UNDERscore”。

有些人使用“魔术方法”这个术语,但在含义方法,特殊方法(见下文)或两者之间的某些方面之间存在某种模糊之处。

官方术语“特殊属性”,它与dunder方法紧密但不完全重叠。参考文献中的Data Model章节从未完全解释特殊属性是什么,但基本思想是它至少是下列之一:

  • 由解释器本身或其内置代码提供的属性,例如函数上的__name__
  • 属于解释器本身实现的协议的属性,例如__add__运算符的+或索引和切片的__getitem__
  • 允许解释器专门查找的属性,忽略实例并直接进入类,例如再次__add__

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

与此同时,大多数dunder方法都是特殊属性,但并非全部 - 特别是stdlib或外部库想要定义自己的协议以同样的方式运行并不常见,例如pickle协议

答案 6 :(得分:0)

[推测] Python受Algol68的影响,Guido可能在University of Amsterdam处使用Algol68,其中Algol68具有类似的“stropping regime”叫做“引用stropping”。在Algol68中,运算符,类型和关键字 可以以不同的字体显示(通常为** 粗体 **,或 __带下划线 em> __),在源代码文件中,这种字体是用引号实现的,例如'abs'(引用类似于'wikitext'中的引用)

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

  • '和'⇒__和__
  • '或'⇒__或__
  • '不是'⇒不是
  • 'entier'⇒__trunc__
  • 'shl'⇒__lshift__
  • 'shr'⇒__rshift__
  • 'upb'⇒__sizeof__
  • 'long'⇒__long__
  • 'int'⇒__int__
  • 'real'⇒__float__
  • 'format'⇒__format__
  • 'repr'⇒__repr__
  • 'abs'⇒__abs__
  • '减去'⇒__neg__
  • '减去'⇒__sub__
  • '加'⇒__add__
  • '次'⇒__mul__
  • 'mod'⇒__mod__
  • 'div'⇒__truediv__
  • 'over'⇒__div__
  • 'up'⇒__pow__
  • '即时'⇒想象
  • '重新'⇒真实
  • 'conj'⇒conjugate

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

我的2美分:¢所以有时候 - 就像一个鬼 - 当你将python类剪切并粘贴到wiki中时,你会神奇地转世Algol68的粗体关键字。 ¢