Python 中的双下划线

2024 年 8 月 29 日 | 阅读 6 分钟

在接下来的教程中,我们将讨论 **双下划线** 及其在 Python 编程语言中的用途。但在那之前,让我们简要讨论一下下划线的一些方面。

理解 Python 下划线

在 Python 中,**下划线字符 (\_)** 并不简单。许多语言使用下划线仅以 snake_case 的方式命名函数和变量;然而,Python 对它有更重要的用途。很可能,我们中的大多数人都熟悉以下语法

  1. for _ in range(20)
  2. __init__(self)
  3. _ = 10

**下划线 (\_)** 字符在不同情况下具有不同的含义。

下划线 (\_) 有以下几种用法

  1. 在解释器中使用下划线
  2. 使用下划线忽略值
  3. 在循环中使用下划线
  4. 使用下划线分隔数字
  5. 为命名使用下划线

但是,我们只涵盖与双下划线相关的命名约定。

这些命名约定分为两种类型

  1. 双前导下划线:**__var**
  2. 双前导和双后导下划线:**__var__**

因此,让我们开始吧。

理解双前导下划线

**双前导下划线** 用于 **名称的混合(name mangling)**。

双前导下划线的语法如下所示

语法

双前导下划线告诉 Python 解释器重写子类属性的名称,以避免任何命名冲突。

**名称混合(Name Mangling)**:Python 解释器以一种在类继承期间难以打乱的方式修改变量的名称。

让我们考虑一个基于此功能的示例。

示例:1

输出

 ['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'_mySample__third',
'_second',
'first']

说明

在上面的代码片段中,我们定义了一个名为 **mySample()** 的类,并使用初始化函数声明了一些值。然后,我们使用 **myObj** 对象实例化了该类。最后,我们打印了对象的目录。

结果,上面的代码块返回了类对象的每个属性。现在,让我们观察属性列表中的变量。

变量 **self.first** 在列表中出现,没有任何更改。

变量 **self._second** 也出现在列表中,没有任何更改。

但是,我们可以观察到变量 **self.__third** 的一些更改。

如果我们观察属性列表,我们会注意到一个名为 **_mySample__third** 的属性。这就是 **名称混合** 的情况。它用于避免子类中变量的重写。

让我们通过另一个示例来理解重写的工作原理,在这个示例中,我们创建了另一个继承了 **mySample** 类功能的类。

示例

输出

 Variable Overridden
Variable Overridden
Traceback (most recent call last):
  File "D:\Python\ternarypy.py", line 24, in 
    print(myobj.__third)
AttributeError: 'AnotherClass' object has no attribute '__third'

说明

在上面的代码片段中,我们定义了一个名为 **mySample()** 的类,并使用初始化函数声明了一些变量。然后,我们为 **mySample()** 类定义了一个子类,在其中使用 **super()** 函数继承了父类的变量并对其进行了重写。最后,我们实例化了子类并打印了重写变量的值。

结果,前两个变量的消息成功打印;但是,在 **"__third"** 变量的情况下,程序引发了一个异常。这是由于名称混合,它将 **myObj.__third 更改为 _AnotherClass__third**。

让我们考虑另一个示例,以便通过修改后的属性打印该元素。

示例:2

输出

 Variable Overridden

说明

在上面的代码片段中,我们可以观察到我们使用了 **"_AnotherClass__third"** 变量而不是 **"__third"** 变量来访问变量的值。

我们可以通过类中的方法来访问双前导下划线变量。让我们考虑一个基于此功能的示例。

例如:3

输出

 Welcome
Traceback (most recent call last):
  File "D:\Python\ternarypy.py", line 15, in 
    print(myObj.__myVar)
AttributeError: 'myClass' object has no attribute '__myVar'

说明

在上面的代码片段中,我们定义了一个类,并使用初始化函数声明了一个变量。然后,我们定义了一个方法来返回变量的值。最后,我们实例化了该类,并尝试通过两种方式打印变量的值。结果,程序在打印方法时返回 **"Welcome"** 语句。但是,它也为另一个方法引发了一个异常,因为它更改了变量的名称。

我们也可以利用双前导下划线来命名方法。让我们考虑一个基于此功能的示例。

示例: 4

输出

 Welcome
Traceback (most recent call last):
  File "D:\Python\ternarypy.py", line 14, in 
    print(myObj.__myfunction())
AttributeError: 'myClass' object has no attribute '__myfunction'

说明

在上面的代码片段中,我们在类中定义了一个后跟双前导下划线的函数。然后,我们定义了另一个函数来从该函数调用它,并向用户打印结果。

现在,让我们理解另一种名称混合的方法。首先,我们将声明一个名为 **_myClass__myVar** 的变量,然后尝试使用双前导下划线名称访问该变量。

让我们看一个例子

示例:5

输出

Welcome

说明

在上面的代码片段中,我们声明了一个变量并定义了一个类。然后,我们定义了一个函数来返回已声明变量的值。最后,我们实例化了该类并调用了该函数来打印该变量的值。

理解双前导和双后导下划线

在 Python 这样的编程语言中,我们会发现许多以双下划线开头和结尾的名称。这些命名约定称为 **魔术方法(Magic methods)** 或 **Dunder 方法(Dunder methods)**。

双前导和双后导下划线的语法如下所示

语法

让我们考虑一个基于魔术方法的示例。

示例

输出

10

说明

在上面的代码片段中,我们定义了一个类。我们在类中定义了一个名为 **__init__()** 的魔术方法,也称为初始化函数。然后,我们将变量声明为 **__num__**。最后,我们实例化了该类并打印了变量的值。结果,这个程序可以工作并产生所需的结果。但是,将魔术方法用作变量名不是一个好习惯,这会导致名称冲突。因此,最好避免使用它们。


下一个主题#