内置 __dict__ 正在返回以 class 名称作为前缀的属性名称

built in __dict__ is returning names of attributes with class names as prefix

class Enrollment(object):

    def __init__(self,enrollmentId=None, enrollmentReference=None):
        self.enrollmentId = enrollmentId
        self.enrollmentReference = enrollmentReference

    @property
    def enrollmentId(self):
        return self.__enrollmentId

    @enrollmentId.setter
    def enrollmentId(self, enrollmentId):
        self.__enrollmentId = enrollmentId

    @property
    def enrollmentReference(self):
        return self.__enrollmentReference

    @enrollmentReference.setter
    def enrollmentReference(self, enrollmentReference):
        self.__enrollmentReference = enrollmentReference

如果我现在尝试打印上面的属性 class:

print(Enrollment().__dict__)

它打印以 class 名称为前缀的属性,如下所示:

{'_Enrollment__enrollmentId': None, '_Enrollment__enrollmentReference': None}

注意:如果我将对象作为超级 class 删除,一切正常,它会正确打印属性,如下所示:

{'enrollmentId': None, 'enrollmentReference': None}

我已经为此苦苦思索了 2 天,但一直没有成功。 无法理解为什么 class 名称是属性的前缀。我需要将 Enrollment 对象序列化为 JSON.

在 class 定义中,Python 将 __x 转换为 _classname__x。这叫做name mangling。它的目的是支持 class 本地引用,这样子 class 就不会无意中破坏父 class.

的内部逻辑

原因

这是由于 Python 的 name mangling 个 class 属性名称以 __ 开头并且最多以一个 _ 作为后缀.这表明对这些属性有更严格的隐私保护。

注意:这仍然是一种启发式方法,不应指望用于防止访问。

来自文档:

Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped.

class A:
    def __foo_(self): pass

print(vars(A))

产出

{'__module__': '__main__', '_A__foo_': <function A.__foo_ at 0x1118c2488>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

请注意 __foo_ 已被破坏为 _A__foo_

使用

为什么这可能有用?那么文档中的示例非常引人注目:

Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls. For example:

class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update   # private copy of original update() method

class MappingSubclass(Mapping):

    def update(self, keys, values):
        # provides new signature for update()
        # but does not break __init__()
        for item in zip(keys, values):
            self.items_list.append(item)

tldr

阅读the docs on name mangling