Python 各种下划线的含义

1. 单前导下划线 _xxx

  Python代码遵循一个规范:以单下划线开头的变量或方法应被视为非公开的API,用于指定该名称为“私有”,只供内部使用,外部调用者不应该去访问以单下划线开头的变量或方法。
  虽然以from xxx import *导入变量或方法时,除模块或包中 __all__列表包含的 以_开头的名称外,其余以_开头的名称都不会被导入,这一定程度上体现了单前导下划线 _xxx 的“私有化”,但是PEP 8编码规范不推荐使用通配符 * 导入,即不推荐from xxx import *的导入方式。离开了from xxx import *的这种导入方式,“单前导下划线 _xxx 只供内部使用”只是一个命名约定成俗的规定,我们依然可以直接访问单前导下划线 _xxx 的变量或方法。观察下例:

class Test:
    def __init__(self, ingredients, radius):
        self.foo = 11
        self._bar = 23       

if __name__ == '__main__':
	t = Test()
	print(t.foo)
	print(t._bar)

运行结果为:
11
23

2. 双前导下划线 __xxx 或 __xxx_

  Python有一个非常简单的机制完成伪私有化功能,这个机制名叫名称转写(name mangling):以双下划线开头,并以最多一个下划线结尾的标识符, 例如__x,会被转写为_classname__x,其中classname为当前类名。观察下例:

class A:   	
	def __mtehod_name(self):
		pass
		

class B(A):
	def __mtehod_name(self):
		pass
	
if __name__ == '__main__':	
	print(dir(B()))

运行结果如下:

['_A__mtehod_name', '_B__mtehod_name', '__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__', '_internal_ues']

  这样,不但可以一定程度上解决“单前导下划线 _xxx 的变量或方法可以被直接访问”的问题,不过依然可以间接通过转写名称来访问;而且还可以有效避免父子类同名方法的命名冲突,如上述同名方法为__mtehod_name,名称转写后分别对应_A__mtehod_name_B__mtehod_name
  上例无法体现名称转写如何有效避免父子类同名方法的命名冲突,改写上例,如若将双前导下划线去掉改为普通函数,父类A增加初始化代码并调用此普通函数,代码如下:

class A:
	def __init__(self):
		self.mtehod_name()     
	
	def mtehod_name(self):
		print("调用了A类的mtehod_name方法")	
		

class B(A):
	def mtehod_name(self):
		print("调用了B类的mtehod_name方法")	
		
		
if __name__ == '__main__':
	b = B()

运行结果为:调用了B类的mtehod_name方法

  但是观察代码可知,创建B类的对象时,由于子类B无初始化方法,需要调用了父类A的__init__()方法进行初始化,而父类的__init__()方法中其实应该调用的是父类的方法mtehod_name(),而此时的运行结果显示调用了子类B类的同名方法mtehod_name(),这显然是不合理的!加回双前导下划线,观察运行结果:

class A:
	def __init__(self):
		self.__mtehod_name()     
	
	def __mtehod_name(self):
		print("调用了A类的__mtehod_name方法")	
		

class B(A):
	def __mtehod_name(self):
		print("调用了B类的__mtehod_name方法")	
		
		
if __name__ == '__main__':
	b = B()

运行结果:调用了A类的__mtehod_name方法

体现出双前导下划线__xxx有效避免父子类同名方法的命名冲突!

3. 单末尾下划线 xxx_

  按照PEP 8编程规范:单末尾下划线 xxx_ 也是一个约定,用来避免与python关键字产生命名冲突。例如,如果想用class来用作变量名称,而class又是pytohn 关键字,此时可定义为class_ 。

4. 双前导和双末尾下划线 __var__

  日常开发中,最好避免在自己的程序中使用以双下划线(“dunders”)开头和结尾的名称,因为它是Python语言定义的一种特殊方法(魔法方法),如熟知的__init__ 、_dict_ 、__getitem__等等。如果非要使用这种写法去声明,若声明的变量不是内置的魔法方法,Python会将它当做普通的变量来操作;若和内置方法重名,即重写内置方法,可能会因为功能冲突而引发报错

5. 单下划线 _

①在交互式解释器会话中,代表上一条执行的语句的结果

>>> _
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_' is not defined
>>> 42
42
>>> _
42

这种用法首先被标准CPython解释器采用,然后其他类型的解释器也先后采用。

②在python代码中,作为临时性的名称

应用场景:某些不关心且不会在后面用到的数据,可以采取分配临时名称存着。

例如,我们可能对循环计数中的实际值不感兴趣,只是想要循环遍历相应次数,此时就可以使用“_”。

例一

n = 42
for _ in range(n):
	print('-' * 30)

例二

>>> car = ('red', 12, 3812.4)
>>> color, _, mileage = car

>>> color
'red'
>>> mileage
3812.4
>>> _
12

③字符串之间翻译查找的函数名称
  也许我们曾看到”_“会被作为一个函数来使用。这种情况下,它通常用于实现国际化和本地化字符串之间翻译查找的函数名称,这似乎源自并遵循相应的C约定。例如,在Django文档“转换”章节中,你将能看到如下代码:

from django.utils.translation import ugettext as _ 
from django.http import HttpResponse 
def my_view(request): 
    output = _("Welcome to my site.") 
    return HttpResponse(output)

参考
关于Python的前后、单双下划线作用,看完这篇文章,吊打面试官!
Python中dunder名称的来历

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值