Python教程-Python中的双下划线
在以下教程中,我们将讨论双下划线及其在Python编程语言中的用途。但在此之前,让我们简要讨论下划线的一些方面。
理解Python下划线
在Python中,字符下划线 (_) 不仅仅是一个简单的符号。有许多语言只使用下划线来命名函数和变量,采用蛇形命名法;然而,在Python中,它有更重要的用途。大多数人可能熟悉以下语法:
- for _ in range(20)
- init(self)
- _ = 10
下划线 (_) 字符在不同情况下传达不同的含义。
下划线 (_) 有多种用途,包括:
- 在解释器中使用下划线
- 使用下划线来忽略值
- 在循环中使用下划线
- 使用下划线来分隔数字的位数
- 用于命名的下划线
然而,我们只会讨论与双下划线一起使用的命名约定。
这些命名约定分为两种类型:
- 双前导下划线:__ var
- 双前导和尾随下划线: var
现在,让我们开始吧。
理解双前导下划线
双前导下划线 用于进行名称的修饰。
双前导下划线的语法如下所示:
语法:
__var
双前导下划线告诉Python解释器重写子类属性的名称,以避免命名冲突。
名称修饰: Python解释器以一种难以在类继承过程中混淆的方式更改变量的名称。
让我们通过一个基于此功能的示例来了解。
示例:1
# defining a class
class mySample():
# using the __init__() function
def __init__(self):
self.first = 10
self._second = 15
self.__third = 20
# instantiating the class
myObj = mySample()
# printing the directory of the object
print(dir(myObj))
输出:
['__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 类的功能。
示例:
# defining a class
class mySample():
# using the __init__() function
def __init__(self):
self.first = 10
self._second = 15
self.__third = 20
# defining a child class
class AnotherClass(mySample):
def __init__(self):
super().__init__()
self.first = "Variable Overridden"
self._second = "Variable Overridden"
self.__third = "Variable Overridden"
# instantiating the child class
myobj = AnotherClass()
# printing the values of variables
print(myobj.first)
print(myobj._second)
print(myobj.__third)
输出:
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
# defining a class
class mySample():
# using the __init__() function
def __init__(self):
self.first = 10
self._second = 15
self.__third = 20
# defining a child class
class AnotherClass(mySample):
def __init__(self):
super().__init__()
self.first = "Variable Overridden"
self._second = "Variable Overridden"
self.__third = "Variable Overridden"
# instantiating the child class
myobj = AnotherClass()
# printing the value(s) of variable(s)
print(myobj._AnotherClass__third)
输出:
Variable Overridden
解释:
在上面的代码段中,我们可以看到我们使用 "_AnotherClass__third" 变量来访问变量的值,而不是使用 "__third" 变量。
我们可以使用类中的方法来访问双前导下划线变量。让我们考虑一个基于这个功能的示例。
示例:3
# defining the class
class myClass:
# initializing function
def __init__(self):
self.__myVar = "Welcome"
# defining another method to return the variable
def get_Var(self):
return self.__myVar
# instantiating the class
myObj = myClass()
# it returns the "Welcome" which is a __var
print(myObj.get_Var())
# here, an error is raised as stated before. It alters the variable's name
print(myObj.__myVar)
输出:
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
# defining a class
class myClass:
# defining a double-leading underscore function
def __myfunction(self):
return "Welcome"
# defining a function to call the above function
def call_function(self):
return self.__myfunction()
# instantiating the class
myObj = myClass()
# printing the value within the function
print(myObj.call_function())
# raised an error
print(myObj.__myfunction())
输出:
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
# declaring a variable
_myClass__myVar = "Welcome"
# defining a class
class myClass:
# defining a function to return the declared variable
def call_function(self):
return __myVar
# instantiating the class
myObj = myClass()
# printing the value of the variable
print(myObj.call_function())
输出:
Welcome
解释:
在上面的代码段中,我们声明了一个变量并定义了一个类。然后,我们定义了一个函数来返回已声明变量的值。最后,我们实例化了该类并调用函数以打印该变量的值。
理解双前导和尾随下划线
在像Python这样的编程语言中,我们会发现许多以双下划线开头和结束的名称。这些命名约定称为魔术方法或Dunder方法。
双前导和尾随下划线的语法如下所示:
语法:
__var__
让我们考虑一个基于魔术方法的示例。
示例:
# defining a class
class myClass:
# using a magic method
def __init__(self):
# using a magic method as variable name
self.__myNum__ = 10
# instantiating the class
myObj = myClass()
# printing the value of the variable
print(myObj.__myNum__)
输出:
10
解释:
在上面的代码段中,我们定义了一个类。我们在类中使用了一个魔术方法,即初始化函数 init()。然后,我们将变量命名为 num。最后,我们实例化了该类,并打印了变量的值。结果是,该程序可以正常运行并产生所需的输出。然而,使用魔术方法作为变量名不是一个好的实践,因为这会导致命名冲突。因此,最好避免使用它们。