python name mangling mechanism
Name Mangling
副标题:python中对class的private支持
这是在各类计算机语言中广泛使用的用于避免名称冲突的机制,通过添加更多的语义信息到变量名中来实现避免名称冲突,python也提供了这种机制,主要是通过避免子类重写父类某些属性,来支持python类中private的实现
预备知识
-
类的private属性,在C++和Java中你可以明确的声明一个某成员是private的,private成员的意义在于:该成员只能够从类中访问,也就是说,一方面,使用该类的用户不能从外部直接访问该成员,另一方面,private成员是不会被该类的子类继承的,同时也表明子类不能够重写父类的private成员(没有继承过来何谈重写),也就是说子类中定义的同名成员仅仅是子类的成员而已跟父类无关
-
多态,从C++观点来看,不同子类对父类的某个接口进行了不同的逻辑实现,这样对象在运行时由于属于不同的子类就会表现出不同的行为,这种现象称其为多态
-
python类成员的特殊性,从C++观点来看,python中所有的类成员都是public的(即都可从外部访问且都可被子类继承)并且都是virtual(即都可子类被重写)
python为什么要提供Name Mangling机制
python对class没有提供强制的private机制(即强制属性私有化,只能在class内访问的机制),也就是说父类中的一切属性都会被子类继承重写,假设这样一个情形,父类中有方法:show()
和tick()
,并且show()
中调用了tick()
,子类中重写了方法tick()
,这时子类的实例化对象调用show()
的效果跟父类中定义的show()
是不同的(因为被调用到的tick()
逻辑发生了变化),至此,对上述现象有两种解读:一,如果父类期望方法tick()
被子类重写,那么上面的现象就是多态,二,如果父类只是将tick()
当作自己使用的方法并不期望被子类重写呢,那么上面的现象就是一个错误,Name Mangling机制正是针对第二种情况的解决方案,其目的就是避免子类重写父类不期望子类重写的属性
python中Name Mangling语法
class中以两个下划线开头且至多以一个下划线结尾的属性将被进行mangling处理
mangling处理,到底干了什么
**原理:**在python解释器从源码构造class对象时,凡是符合mangling语法的属性都被重命名,例如:类MyClass中的__my
属性被重命名为_MyClass__my
,就为属性添加前缀:一个下划线加当前类名,并且在类中凡是引用到该属性的地方都进行了替换
**示例1:**对属性重命名
# Class Define
class MyClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
# Notice: 这个方法将被mangling处理
def __cal(self):
self.total = self.x + self.y # 加法
def show(self):
self.__cal() # 这里也会进行mangling处理
print(self.total)
# In Python Interpeter, you should import MyClass first
>>>MyClass.__cal
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'MyClass' has no attribute '__cal'
# 因为属性__cal已经转换为_MyClass__cal所以上面报错,执行下面的则不会报错
>>>MyClass._MyClass__cal
**示例2:**子类’不能’重写父类的mangling属性
# 定义一个继承自上面MyClass类的子类
class DerivedMyClass(MyClass):
# 定义一个跟父类同名的方法,其实该方法并不会重写父类方法
# 因为该方法mangling后为_DerivedMyClass__cal,而父类
# 同名方法mangling后为_MyClass__cal,因为经过mangling
# 处理后方法名是不同的,所以子类是不能重写父类的__cal方法的,
# 其实通过mangling机制实现对private支持并不是强制的,因为
# 你还是可以在子类通过重写父类方法_MyClass__cal来打破私有
# 限制,可以看出python类的设计思想是:假设用户是明智的
def __cal(self):
self.total = self.x * self.y # 乘法
# In Python interpeter, you should import MyClass and DerivedMyClass first
>>>d = DerivedMyClass(5, 7)
>>>d.show()
12 # 结果是使用父类的__cal的加法计算的,而不是子类__cal的乘法计算的
**示例3:**如果一个方法既具有私有性又具有公有性
# Class Define
class MyClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
# 注意:方法名由__cal变成了cal
# 这样使得子类中可以重写方法cal
# 同时父类中仍可使用不被重写的cal逻辑
def cal(self):
self.total = self.x + self.y
def show(self):
self.__cal()
print(self.total)
__cal = cal # 通过赋值重用方法