【问题标题】:Why does python use two underscores for certain things?为什么 python 在某些事情上使用两个下划线?
【发布时间】:2010-08-09 18:34:45
【问题描述】:

我对实际的编程语言相当陌生,Python 是我的第一个。我对 Linux 有一点了解,足以用它找到一份暑期工作(我还在上高中),而且在工作中,我有很多空闲时间用来学习 Python。

但有一件事让我很着迷。当你有诸如

之类的表达式时,Python 到底有什么不同?
x.__add__(y) <==> x+y
x.__getattribute__('foo') <==> x.foo

我知道方法的作用和内容,并且知道它们的作用,但我的问题是:上面的双下划线方法与看起来更简单的等效方法有何不同?

P.S.,我不介意被讲授编程历史,事实上,我觉得了解它非常有用 :) 如果这些主要是 Python 的历史方面,请随意开始漫谈。

【问题讨论】:

标签: python syntax


【解决方案1】:

这里是Python的创建者explaining it

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

...

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 很高兴知道,但 Python 是我的第一门编程语言。我不知道什么是“私人”或名称修饰,正如 delnan 在下面的评论中发布的那样。不过谢谢,一旦我这样做了,这个答案对我来说会好得多。
    • 据我所知,您通常不会使用 __ 导致私有,而是在您的函数可能会踩到另一个名称空间时使用它。考虑到这一点,我看到有人说要使用单个下划线将其标记为“实现细节”。
    【解决方案3】:

    好吧,程序员的能力很好,所以应该有一种方法来自定义行为。像运算符重载(__add____div____ge__,...),属性访问(__getattribute____getattr__(这两个是不同的),__delattr__,...)等等。情况,如运算符,通常的语法将 1:1 映射到相应的方法。在其他情况下,有一个特殊的过程在某些时候涉及调用相应的方法 - 例如,__getattr__ 仅在对象不具有请求的属性并且__getattribute__ 未实现或引发 AttributeError 时才被调用。其中一些是非常高级的主题,可以让您深入了解对象系统的核心,并且很少需要。因此,无需全部学习,只需在需要/想知道时查阅参考资料。说到参考,here it is

    【讨论】:

    • 你很擅长解释事情,谢谢!但是有一个问题,你能说出一个常见的案例(可能是一个代码示例的链接),这些双下划线方法的作用与运算符不同吗?
    • 嗯?作为运算符的特殊方法会立即被调用(afaik)。当然还有很多其他的不代表运算符,比如我们都已经命名的__getattribute____enter__/__exit__ for with 语句(with open("file") as f: do_stuff(f))。在这些情况下,有一些更复杂的规则,文档比我更了解它们。
    • 我同意 S,实际 的解释是在您阅读了链接位之后才有意义 之后才出现的.这就是为什么你会“嗯?”在您的第一条评论中。
    【解决方案4】:

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

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

    【讨论】:

    • 简洁明了。除了自定义类之外,它们还会在 Python 中常见的其他地方出现吗?
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      许多其他问题现在被标记为该问题的重复项,其中至少有两个询问__spam__ 方法被称为什么,或者约定被称为什么,而现有的答案都没有涵盖这一点,所以:

      实际上两者都没有正式名称。

      许多开发人员非正式地称它们为“dunder 方法”,即“Double UNDERscore”。

      有些人使用“魔法方法”这个词,但它的含义有点模糊,指的是dunder方法、特殊方法(见下文)或两者之间的某个地方。


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

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

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

      同时,大多数 dunder 方法都是特殊属性,但不是全部 - 特别是,stdlib 或外部库想要定义自己的以相同方式工作的协议并不少见,例如 pickle 协议。

      【讨论】:

        【解决方案7】:

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

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

        • '和' ⇒ __and__
        • '或' ⇒ __or__
        • '不是'⇒ 不是
        • 'entier' ⇒ __trunc__
        • 'shl' ⇒ __lshift__
        • 'shr' ⇒ __rshift__
        • 'upb' ⇒ __sizeof__
        • '长'⇒ __long__
        • 'int' ⇒ __int__
        • “真实”⇒ __float__
        • '格式'⇒ __format__
        • 'repr' ⇒ __repr__
        • 'abs' ⇒ __abs__
        • '减号'⇒ __neg__
        • '减号'⇒ __sub__
        • '加号'⇒ __add__
        • '次'⇒ __mul__
        • 'mod' ⇒ __mod__
        • 'div' ⇒ __truediv__
        • “结束”⇒ __div__
        • '向上'⇒ __pow__
        • “我”⇒ 想像
        • '是'⇒真实的
        • 'conj' ⇒ 共轭

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

        我的 2 美分:¢ 所以有时——就像鬼一样——当你将 python 类剪切并粘贴到 wiki 中时,你会神奇地重生 Algol68 的 bold 关键字。 ¢

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-29
          • 1970-01-01
          • 2016-08-07
          • 2013-02-12
          • 2016-10-29
          • 2016-01-27
          • 1970-01-01
          相关资源
          最近更新 更多