为什么在类方法中所有双下划线变量都被混淆?

3

编辑:现有的回答都谈到了类内部存在名称修饰的事实。我的问题是关于类方法内的值——请注意,方法内的解析与类范围内的解析不同,否则__CLASS将会被打印。


这在Python 2.7和3.6中似乎是个问题。

例如,以下代码:

__GLOBAL = 'global'
_Bar__MANGLED_GLOBAL = 'mangled global'

class Bar(object):
    __CLASS = 'class'

    def baz(self):
        __LOCAL = 'local'
        try:
            print __LOCAL
        except Exception as e:
            print e
        try:
            print __CLASS
        except Exception as e:
            print e
        try:
            print __GLOBAL
        except Exception as e:
            print e
        try:
            print __MANGLED_GLOBAL
        except Exception as e:
            print e

Bar().baz()

我会提供

local
global name '_Bar__CLASS' is not defined
global name '_Bar__GLOBAL' is not defined
mangled global

我理解当存在other.__x或需要一个类级别的__fooself.__foo相等时的逻辑,但这似乎是一种疏忽,因为在类范围内的任何内容都会自动重写。


2
这可能与实现的复杂性有关。没有感知到需要未混淆的本地变量,这就没有证明需要进行区分。 - chepner
1
一般来说,无法确定给定的名称查找是否会在运行时应用于当前类的实例。因此,名称修饰应适用于所有名称。 - jasonharper
1
@jasonharper 我认为这并不完全正确。问题不在于不能确定 x.__foo 中的 x 是否是当前类的实例。__FOO 明显不是属性查找,因为没有涉及到 .。这样的名称是否可能引用与属性相同的对象并不重要,因为名称改编旨在避免覆盖继承的属性。局部变量不受继承影响。 - chepner
1
即使这只是一个疏忽,"修复"它的成本也可能超过其收益。有人真的那么渴望双下划线前缀的本地名称吗? - chepner
@chepner 请看下面我的回答,Python中名称混淆的完整名称是“私有名称混淆”,适用于类中的所有名称。 - LiuXiMin
显示剩余2条评论
2个回答

3
这并非疏忽。实际上,在教程中曾明确宣传过私有全局变量:tutorial

现在支持有限的类私有标识符。任何以 __spam 的形式(至少两个前导下划线,最多一个尾随下划线)的标识符,都会被文本替换为 _classname__spam,其中 classname 是当前类名去除前导下划线后的部分。这种替换操作不考虑标识符的语法位置,因此可以用于定义类和实例的私有变量、方法,甚至是全局变量,甚至可以将该类的实例变量存储在 其他 类的实例上。

没有具体的参考资料,比如邮件列表对话等等,我们只能推测其设计原因。(我检查了提交历史记录,但原始提交没有说明设计选择的理由。)我们可以推测它是为了实现私有全局变量之类的东西,但我们也可以推测它只是以这种方式最容易实现,并且如果有其他更容易的选择,实现者不会受到私有全局变量的影响。在只影响类的实例和类属性的名称混淆的实现方面,这肯定会更难一些。

2
更新:起初,我并没有真正回答问题,但是根据线索,最终找到了真正的答案,所以请耐心阅读直到结尾。简而言之,名称混淆适用于出现在类中的所有名称。 Python类不能拥有真正的私有属性,前缀__用于private目的。
我看到以下建议:

但是尽量避免使用__private形式。我从不使用它。相信我。如果你使用它,你以后后悔的。

和:

形式__private只会触发一个私有名称混淆,其目的是防止子类中意外的命名空间冲突:MyClass.__private只变成了MyClass._MyClass__private

像Pythonista一样编码:惯用的Python命名 我查看了私有名称混淆,根据它,混淆适用于类中出现的所有names

私有名称混淆:当在类定义中文本上出现的标识符以两个或多个下划线字符开头并且不以两个或多个下划线结尾时,它被视为该类的private name

因此,似乎混淆适用于出现在类中的所有名称。这就是为什么在类方法中所有双下划线变量都被混淆的原因。

我不确定我是否理解你的论点。虽然它们没有真正的私有变量(只是被混淆了),但这与你答案的后半部分或我的问题有什么关系呢? - Cireo
@Cireo 你可能想查看我分享的链接。 - LiuXiMin
为什么您写的“名称修饰适用于类中出现的所有名称”,而您提供的链接明确说明它仅适用于以至少两个下划线开头且不以至少两个下划线结尾的名称? - wovano
@wovano 是的,你说得对。我也没有错。但我想我表达得不够清楚。我的观点是,名称修饰适用于类中出现的所有名称,即使这些名称没有在类中定义。当然,名称修饰有自己的规则:只适用于以至少两个下划线开头且不以至少两个下划线结尾的名称。Python 将尝试对类中使用的所有名称进行名称修饰。抱歉我的英语写作不好。 - LiuXiMin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接