简体   繁体   English

Python 名称修改允许双向访问

[英]Python name mangling allows access both ways

So I have come across a very interesting behavior of python name mangling.所以我遇到了一个非常有趣的 python 名称修改行为。 Consider the following code考虑以下代码

class C:
    def __init__(self):
        self.__c = 1
    
    @staticmethod
    def change(instance):
        print(dir(instance))
        print(instance.__c)
        print(instance._C__c)

Here I create a private field __c and expect to have direct access to it from within class and access via _C__c from outside of the class.在这里,我创建了一个私有字段 __c 并希望可以从 class 内部直接访问它,并从 class 外部通过 _C__c 访问它。 So, if we pass an instance of C to C.change either 2nd or 3rd print should fail.因此,如果我们将 C 的实例传递给 C。更改第二次或第三次打印都应该失败。
Lets check:让我们检查:


>>> c =  C()
>>> dir(c)
['_C__c', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'change']
>>> C.change(c)
['_C__c', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'change']
1
1

First, for debug we print all available members of c with dir(c) .首先,为了调试,我们使用dir(c)打印 c 的所有可用成员。 Then we call C.change passing it the variable c .然后我们调用 C.change 传递变量c
Hmm, that is unexpected, no errors.嗯,这是意料之外的,没有错误。
So, first print in change shows us all the available entries of the instance object.因此,第一个 print in change向我们展示了instance object 的所有可用条目。 Here we see that field __c is available as _C__c .在这里,我们看到字段__c可用作_C__c That seems ok, since we access not through self, but through another variable.这似乎没问题,因为我们不是通过自我访问,而是通过另一个变量。
Having such output from 'dir' I expect print(instance.__c) to fail with AttributeError.从“dir”获得这样的 output 我希望print(instance.__c)会因 AttributeError 而失败。
However, unexpectedly, it works just fine!然而,出乎意料的是,它工作得很好!
This really confuses me, since I do not understand, why is __c accessible and if it is so by design, then why is it not listed in dir output?这真的让我很困惑,因为我不明白,为什么__c可以访问,如果是这样设计的,那么为什么它没有在dir output 中列出?

Whenever you write __c inside a class, it will be textually replaced by _<classname>__c .每当您在 class 中写入__c时,它将在文本上被_<classname>__c替换。 It's not dynamically performed, it's done at the parsing stage.它不是动态执行的,它是在解析阶段完成的。 Hence, the interpreter won't ever see __c , only _<classname>__c .因此,解释器永远不会看到__c ,只会看到_<classname>__c That's why only _C__c appears in dir(instance) .这就是为什么只有_C__c出现在dir(instance)中。

Quoting the docs :引用文档

[...] Private names are transformed to a longer form before code is generated for them. [...] 在为其生成代码之前,私有名称会被转换为更长的形式。 The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name.转换插入 class 名称,在名称前面删除了前导下划线并插入了一个下划线。 For example, the identifier __spam occurring in a class named Ham will be transformed to _Ham__spam .例如,出现在名为Ham的 class 中的标识符__spam将转换为_Ham__spam This transformation is independent of the syntactical context in which the identifier is used.此转换与使用标识符的语法上下文无关。 [...] [...]

For that reason, it only applies to dotted attribute access ( xy ), not to dynamic access via (get|set)attr :因此,它仅适用于点属性访问 ( xy ),不适用于通过(get|set)attr进行的动态访问:

>>> class Foo:
...     def __init__(self):
...         setattr(self, '__x', 'test')
... 
>>> Foo().__x
'test'

__ -prefixed names work fine as-is inside the class's own methods. __前缀名称类自己的方法中可以正常工作。 It's only outside the class (including in its subclasses) that the modified name is needed to access the attribute, due to name-mangling .由于name-mangling ,只有class (包括在其子类中)之外,才需要修改后的名称才能访问该属性。

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

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