简体   繁体   English

python私有属性名称重整继承

[英]python private attribute name mangling inheritance

This is an example from "Effective Python", that I am clearly missing something on. 这是“有效Python”中的一个示例,显然我遗漏了一些东西。 I added a few print's to help convince myself, but I'm still a little unclear. 我添加了一些印刷品以帮助说服自己,但我仍然不清楚。

I understand that when you attempt to access an inherited private variable, it fails because in the instance dictionary of the child, the name has been mangled (last line below, attempting to access a.__value rightfully fails because the instance dict contains the mangled version _ApiClass__value ). 我了解,当您尝试访问继承的私有变量时,它会失败,因为在子代的实例字典中,名称已被篡改(下面的最后一行,尝试访问a.__value失败,因为实例dict包含被篡改的版本_ApiClass__value )。

Where I'm getting tripped up is why the inherited method get doesn't have this issue. 我绊倒的地方是为什么继承的方法get没有这个问题。 If you print self.__dict__ in the call to get , you can see that we're still working with the same Child instance dict as we would be if we attempted to use dotted access directly from the child (which contains the mangled name only). 如果在get的调用中打印self.__dict__ ,您会看到我们仍在使用相同的Child实例dict,就像我们尝试直接从子级(仅包含错误的名称)使用点分访问一样。 Dotted attribute access from within this method somehow properly translates to the mangled name and retrieves the private variable. 从此方法中对点属性的访问以某种方式正确地转换为错误的名称并检索私有变量。

My understanding of attribute access is that under the covers, what is essentially happening (albeit simplified) is a.__value is basically a.__dict__['__value'] . 我对属性访问的理解是,在幕后,实际上发生了(尽管简化了) a.__value基本上a.__dict__['__value'] This makes sense and is proved out when you attempt to directly access the inherited private variable, as it fails since only the mangled name is in the Child dict. 这是有道理的,并在您尝试直接访问继承的私有变量时得到了证明,因为它失败了,因为在Child dict中只有错误的名称。 However the inherited method get , which is operating on the same instance dict from Child, works with dotted access so it is clearly not doing a.__dict__['__value'] , but rather a.__dict__['_ApiClass__value'] . 但是,继承自get的方法(对Child的同一实例dict进行操作)可以使用点分访问方式,因此显然它不是在执行a.__dict__['__value'] ,而是在执行a.__dict__['_ApiClass__value']

What is the distinction here that causes the private attribute access from within the get method to be aware of the mangled name as opposed to similar attribute access from the child? 此处的区别是什么导致从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'

Name mangling is done at byte code compilation time, so the name mangling depends on where the function was defined, not what it was called through. 名称修饰在字节码编译时完成,因此名称修饰取决于函数的定义位置,而不是通过函数调用的内容。 Child doesn't have its own get method, it's using ApiClass 's, and ApiClass 's get was mangled to work with ApiClass . Child没有自己的get方法,它使用的是ApiClass ,而ApiClassget被修改为与ApiClass一起ApiClass

This is intentional. 这是故意的。 The goal here is that methods defined in class X mangle for X no matter how you reach them. 这里的目标是,无论您如何到达X类,都必须在X类中定义方法。 If they didn't, and a parent and child both defined a private variable with the same name, the parent wouldn't have private access to its own unique version of the variable, it would be sharing it with the child (even though the meaning of the variable might be completely different in each case). 如果没有,并且父母和子女都用相同的名称定义了一个私有变量,则父母将无法对其变量的唯一版本进行私有访问,它将与孩子共享(即使每种情况下变量的含义可能完全不同)。

The dis module can demonstrate the fact that the mangling is at compile time: dis模块可以证明错误在于编译时:

class Parent:
    def x(self):
        return self.__x

class Child(Parent):
    pass

Then interactively inspecting: 然后进行交互式检查:

>>> import dis
>>> dis.dis(Parent.x)
  3           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (_Parent__x)
              6 RETURN_VALUE
>>> dis.dis(Child.x):
  3           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (_Parent__x)
              6 RETURN_VALUE

Note that the LOAD_ATTR value, _Parent__x is hardcoded into the byte code. 请注意, LOAD_ATTR_Parent__x被硬编码为字节码。

You can also demonstrate how no special behaviors are involved in plain functions (as opposed to methods defined as part of a class): 您还可以演示普通函数(与定义为类的一部分的方法相对)如何不涉及特殊行为:

>>> def foo(bar): return bar.__x
>>> dis.dis(foo)
  1           0 LOAD_FAST                0 (bar)
              3 LOAD_ATTR                0 (__x)
              6 RETURN_VALUE

where the LOAD_ATTR is just trying to load the plain __x name, not a mangled version; LOAD_ATTR只是试图加载普通的__x名称,而不是错误的版本; if bar was an instance of a class, it's highly unlikely this would work thanks to the name mangling protections. 如果bar是一个类的实例,则由于名称修改保护,这几乎不可能实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM