0
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

If i now try to print the attributes of the above class:

print(Enrollment().__dict__)

It prints the attributes prefixed with class name as below:

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

Note: If I remove object as the super class, all works well and it prints the attributes correctly as below:

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

I have been been wrapping my head around this for 2 days now with no luck. Not able to understand why the class name is prefixed to attributes. I need to serialize the Enrollment object to JSON.

Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
aby
  • 95
  • 1
  • 11

2 Answers2

1

In a class definition, Python transforms __x into _classname__x. This is called name mangling. Its purpose is to support class local references so that subclasses don't unintentionally break the internal logic of the parent class.

Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • 2
    Thanks for compact/ordered dicts. – Alex Mar 03 '18 at 08:12
  • @Raymond - Thanks for the short and precise reply. I am puzzled why it does not happen with old style class and how can i get around with it in new style classes.I need a dictionary of attributes to pass on to json.dumps serializer – aby Mar 03 '18 at 08:17
  • The single underscore worked for me. – aby Mar 06 '18 at 23:04
0

Cause

This is due to Python's name mangling of class attribute names that start with __ and are suffixed with at most a single _. This suggests a stricter privacy for these attributes.

Note: This is still a heuristic and shouldn't be counted on for access prevention.

From the docs:

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

outputs

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

Notice that __foo_ has been mangled into _A__foo_.

Uses

Why might this be useful? Well the example from the docs is pretty compelling:

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

Read the docs on name mangling.

Alex
  • 18,484
  • 8
  • 60
  • 80