【问题标题】:Python name mangling allows access both waysPython 名称修改允许双向访问
【发布时间】:2022-01-25 13:25:14
【问题描述】:

所以我遇到了一个非常有趣的 python 名称修改行为。 考虑以下代码

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

在这里,我创建了一个私有字段 __c 并希望可以从类内直接访问它,并从类外通过 _C__c 访问它。因此,如果我们将 C 的实例传递给 C.change,第二次或第三次打印都应该失败。
让我们检查一下:


>>> 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

首先,为了调试,我们使用dir(c) 打印c 的所有可用成员。然后我们调用 C.change 将变量 c 传递给它。
嗯,出乎意料,没有错误。
因此,change 中的第一次打印向我们展示了 instance 对象的所有可用条目。在这里,我们看到 __c 字段可用作 _C__c。这似乎没问题,因为我们不是通过自我访问,而是通过另一个变量。
从 'dir' 得到这样的输出,我希望 print(instance.__c) 会因 AttributeError 而失败。
然而,出乎意料的是,它工作得很好!
这真的让我很困惑,因为我不明白,为什么__c 可以访问,如果设计如此,那么为什么它没有在dir 输出中列出?

【问题讨论】:

    标签: python python-3.x private member name-mangling


    【解决方案1】:

    每当您在类中写入__c 时,它都会在文本上被_<classname>__c 替换。它不是动态执行的,它是在解析阶段完成的。因此,解释器永远不会看到__c,只会看到_<classname>__c。这就是为什么_C__c 只出现在dir(instance) 中的原因。

    引用the docs:

    [...] 在为它们生成代码之前,私有名称被转换为更长的形式。转换插入类名,删除前导下划线并在名称前面插入一个下划线。例如,出现在名为Ham 的类中的标识符__spam 将转换为_Ham__spam。这种转换与使用标识符的语法上下文无关。 [...]

    因此,它仅适用于点属性访问(x.y),不适用于通过(get|set)attr进行的动态访问:

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

    【讨论】:

      【解决方案2】:

      __-prefixed 名称可以正常工作,因为类自己的方法中。由于name-mangling,只有在类(包括其子类中)外部需要修改后的名称才能访问该属性。

      【讨论】:

      • 那为什么我在change方法里面调用它的时候没有在dir(instance)中列出呢?
      • @VladimirVislovich 每当您在类中编写__c 时,它将在文本上 替换为_<classname>__c。它不是动态执行的,它是在解析阶段完成的。这就是为什么_C__c 只出现在dir(instance) 中的原因。
      • @a_guest 哦,现在说得通了!也许你应该让你的评论成为问题的答案,因为它确实是一个
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-19
      • 1970-01-01
      • 1970-01-01
      • 2015-10-09
      相关资源
      最近更新 更多