双下划线开头和结尾的名称(比如 __variable__
或 __method__()
)是 Python 中的特殊命名约定,通常被称为“双下划线名称重整”(name mangling)。它主要有两个方面的作用:名称修饰和限制访问。
-
名称修饰:
在类定义中,双下划线名称重整用于防止子类意外覆盖父类的属性或方法。当一个类的属性或方法以双下划线开头但不以双下划线结尾时(例如
__variable
或__method()
),Python 会将其重整为_ClassName__variable
或_ClassName__method()
的形式。这样做的目的是在子类中防止意外地覆盖父类的属性或方法。例如:
class SuperClass:def __init__(self):self.public_attr = "Public Attribute"self.__private_attr = "Private Attribute"def __private_method(self):print("This is a private method.")def public_method(self):print("This is a public method.")self.__private_method()print(self.__private_attr)class SubClass(SuperClass):def __init__(self):super().__init__()self.__private_attr = "Subclass Private Attribute"def __private_method(self):print("This is a subclass private method.")def use_functions(self):self.public_method()# 在子类中访问父类的私有属性和方法print(self._SuperClass__private_attr)self._SuperClass__private_method()print(self.__private_attr)self.__private_method()# 创建子类实例
sub_instance = SubClass()
sub_instance.use_functions()
在这个示例中:
SuperClass 定义了一个公有属性 public_attr
、一个私有属性 __private_attr
和两个方法 __private_method()
和 public_method()
。
SubClass 继承自 SuperClass,并重新定义了 __private_attr
和 __private_method()
。
在 SubClass 的 use_functions() 方法中,我们调用了父类的 public_method(),并尝试访问父类的私有属性和方法。
由于名称重整的原因,在子类中无法直接访问父类的 __private_attr
和 __private_method()
。但是,我们可以使用 _SuperClass__private_attr
和 _SuperClass__private_method()
来访问它们。
运行这个代码,输出结果如下:
This is a public method.
This is a private method.
Private Attribute
Private Attribute
This is a private method.
Subclass Private Attribute
This is a subclass private method.
-
限制访问:
双下划线名称重整也可用于模拟私有成员,即限制对属性和方法的直接访问。虽然在 Python 中没有严格的私有性,但是通过名称重整可以模拟私有成员,使其不容易被外部访问。
class MyClass:def __init__(self):self.__private_var = 10def __private_method(self):print("This is a private method.")obj = MyClass() print(obj.__private_var) # 会报错,无法直接访问私有变量 obj.__private_method() # 会报错,无法直接访问私有方法
在这个例子中,直接访问
obj.__private_var
和obj.__private_method()
会抛出错误,因为它们被重整为_MyClass__private_var
和_MyClass__private_method()
,而不是直接的名称。
双下划线名称重整的使用有助于确保代码的健壮性和安全性,尤其在大型项目中。然而,应该注意不要滥用双下划线名称重整,因为它可能会导致代码难以理解和维护。
私有”属性存在两种不同的命名规则(单下划线和双下划线),这一事实引出了一个显而易见的问题:应该使用哪种风格?
对于大部分代码而言,我们应该让非公有名称以单下划线开头
。但是,如果我们知道代码中会涉及子类化处理,而且有些内部属性应该对子类进行隐藏,那么此时就应该使用双下划线开头
。- 此外还应该指出的是,有时候可能想定义一个变量,但是名称可能会和保留字产生冲突。基于此,
应该在名称最后加上一个单下划线以示区别
。比如:
lambda_= 2.0 # Trailing _ to avoid clash with lambda keyword
这里不采用以下划线开头的原因是避免在使用意图上发生混淆(例如,如果采用下划线开头的形式,那么可能会被解释为这么做是为了避免名称冲突,而不是作为私有数据的标志)。在名称尾部加一个单下划线就能解决这个问题