Python单下划线与双下划线命名规范深度解析
本文深入解析Python中单下划线与双下划线的命名规则。单下划线表示受保护的变量,建议外部不直接访问;双下划线触发名称修饰机制,实现类级别的私有化,防止子类覆盖。同时介绍以双下划线开头和结尾的特殊方法标识,帮助开发者遵循Python最佳实践,避免命名冲突并理解底层实现。
在Python学习过程中,许多开发者对方法或变量前添加的下划线数量感到困惑,例如常见的 __this__ 形式。在深入探究之前,许多人误以为这些下划线的作用类似于Golang中通过大小写控制可见性,或类似其他语言中的 private 和 public 关键字。然而,Python的设计初衷并非如此简单。以下将详细分析其具体机制。
Python中主要存在四种基于下划线的命名模式。
1. object:表示公用方法或变量。 2. __object__:表示内建的特殊方法,用户应避免自行定义此类名称。 3. __object:表示全私有变量,受到严格保护。 4. _object:表示半保护变量。
核心编码风格建议:避免使用下划线作为变量名的起始字符。
由于下划线对Python解释器具有特殊意义,且常用于内建标识符,建议程序员避免以下划线开头定义变量。通常情况下,以 _object 命名的变量被视为“受保护”的,在模块或类外部不应直接使用,且无法通过 'from module import *' 语句导入。当变量需要体现私有属性时,使用 _object 是一种良好的命名习惯。
通过“单下划线+类名”的机制(例如 _Class__object),可以访问原本看似私有的 __object。由于以双下划线开头的变量名在Python中具有特殊含义,普通变量应避免使用这种命名风格。
以“单下划线”开始的成员变量被称为保护变量,意味着只有类实例和子类实例能够访问这些变量;而以“双下划线”开始的成员变量则是私有成员,意味着只有类实例本身可以访问,连子类实例也无法直接访问这些数据(具体表现见下文示例)。
以单下划线开头的 _foo 代表不能直接访问的类属性,需通过类提供的接口进行访问,且不能使用“from xxx import *”导入;以双下划线开头的 __foo 代表类的私有成员;而以双下划线开头和结尾的 __foo__ 则代表Python中特殊方法专用的标识符,例如 __init__() 代表类的构造函数。
class Foo():
def __init__():
...
def public_method():
print 'This is public method'
def __fullprivate_method():
print 'This is fullprivate_method'
def _halfprivate_method():
print 'This is halfprivate_method'
f = Foo()
f.public_method() # OK
f.__fullprivate_method() # Error occur
f._halfprivate_method() # OK
f._Foo__fullprivate_method() # OK
从上述示例可以看出,f._halfprivate_method() 可以直接访问,确实如此。但根据Python的约定,应将其视为私有成员,不建议在外部使用(尽管技术上可行)。良好的编程习惯是避免在外部调用此类变量。同时,根据Python官方文档说明,_object 和 __object 的作用域限制在本模块内。
观察以下程序的输出结果:
class A(object):
def __init__(self):
self.__private()
self.public()
def __private(self):
print 'A.__private()'
def public(self):
print 'A.public()'
class B(A):
def __private(self):
print 'B.__private()'
def public(self):
print 'B.public()'
b = B()
初步分析
正确的输出结果应为:
A.__private() B.public()
我们分别查看类A和类B的属性列表:
>>> print '\n'.join(dir(A)) _A__private __init__ public >>> print '\n'.join(dir(B)) _A__private _B__private __init__ public
为什么类A中会出现名为 _A__private 的属性,而 __private 似乎消失了?这涉及到Python私有变量的“名称修饰”机制。
Python将两个或以上下划线开头且不以两个或以上下划线结尾的变量视为私有变量。在代码生成前,这些变量会被转换为长格式(即变为公有形式)。转换机制如下:在变量名前插入类名,并在最前端添加一个下划线。这就是所谓的私有变量名称修饰(Private name mangling)。例如,类A中的 __private 标识符将被转换为 _A__private,这解释了上一节中 _A__private 出现和 __private 消失的原因。
此外,还有两点补充说明:
首先,由于名称修饰会使标识符变长,当长度超过255个字符时,Python会进行截断,需注意由此可能引发的命名冲突。
其次,当类名全部以下划线命名时,Python不再执行名称修饰。例如:
class ____(object):
def __init__(self):
self.__method()
def __method(self):
print '____.__method()'
print '\n'.join(dir(____))
__class__
__delattr__
__dict__
__doc__
__getattribute__
__hash__
__init__
__method # 没被矫直
__module__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__str__
__weakref__
obj = ____()
____.__method()
obj.__method() # 可以外部调用
____.__method()
现在我们回过头来解释为何会输出“A.__private()”。
经过名称修饰后,类A的代码实际上变成了如下形式:
class A(object):
def __init__(self):
self._A__private() # 这行变了
self.public()
def _A__private(self): # 这行也变了
print 'A.__private()'
def public(self):
print 'A.public()'
由于在类B定义时并未覆盖 __init__ 方法,因此调用的是 A.__init__,即执行了 self._A__private(),自然输出了“A.__private()”。
以下两段代码可进一步增强说服力,帮助理解:
class C(A):
def __init__(self): # 重写 __init__ ,不再调用self._A__private
self.__private()# 这里绑定的是 _C_private
self.public()
def __private(self):
print 'C.__private()'
def public(self):
print 'C.public()'
c = C()
答案:
C.__private()
C.public()
class A(object):
def __init__(self):
self._A__private() # 调用一个没有定义的函数,但可执行
self.public()
def __private(self):
print 'A.__private()'
def public(self):
print 'A.public()'
a = A()
答案:
A.__private()
A.public()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









