这是“有效Python”中的一个例子,我显然遗漏了一些东西。我加了一些指纹来帮助说服自己,但我还是有点不清楚。在
我知道当您尝试访问继承的私有变量时,它会失败,因为在子实例字典中,名称已损坏(下面最后一行,尝试正确访问a.__value
失败,因为实例dict包含损坏的版本_ApiClass__value
)。在
我遇到的问题是继承的方法get
没有这个问题。如果您在对get
的调用中打印self.__dict__
,您可以看到我们仍在使用相同的子实例dict,就像我们试图直接从子实例dict使用点式访问一样(它只包含损坏的名称)。从这个方法中的点式属性访问以某种方式正确地转换为损坏的名称并检索私有变量。在
我对属性访问的理解是,在本质上发生的事情(尽管简化了)是a.__value
基本上是{get
(它在同一实例dict from Child上操作)与点式访问一起工作,因此它显然没有执行a.__dict__['__value']
,而是a.__dict__['_ApiClass__value']
。在
这里的区别是什么使来自get
方法中的私有属性访问知道损坏的名称,而不是从子级访问类似的属性?在
class ApiClass():
def __init__(self):
self.__value = 5
def get(self):
print(self.__dict__['_ApiClass__value']) #succeeds
print(self.__dict['__value']) #fails bc name mangle
return self.__value #how is this translated to '_ApiClass_value'
#but a similar instance lookup fails?
class Child(ApiClass):
def __init__(self):
super().__init__()
self._value = 'hello'
a = Child()
print(a.__dict__)
print(a.get()) #works, but instance dict has no '__value' key?
print(a.__value) #fail bc name mangled to '_ApiClass_value'
名称混乱是在字节码编译时完成的,因此名称混乱取决于函数的定义位置,而不是函数的调用方式。}的{}被损坏以使用{}。在
Child
没有自己的get
方法,它使用的是ApiClass
,而{这是故意的。这里的目标是使在类
X
中定义的方法为X
而损坏,无论您如何到达它们。如果它们没有,并且父变量和子变量都定义了一个同名的私有变量,那么父变量就不能私有访问它自己唯一的变量版本,它将与子变量共享(即使变量的含义在每种情况下可能完全不同)。在dis
模块可以演示在编译时进行损坏的事实:然后交互检查:
^{pr2}$注意,
LOAD_ATTR
值,_Parent__x
是硬编码到字节码中的。在您还可以演示普通函数(与定义为类的一部分的方法不同)中如何不涉及特殊行为:
如果
LOAD_ATTR
只是试图加载普通的__x
名称,而不是损坏的版本;如果bar
是一个类的实例,那么由于名称损坏保护,这是非常不可能的。在相关问题 更多 >
编程相关推荐